]> git.saurik.com Git - apple/security.git/blobdiff - keychain/ckks/CKKSKeychainView.m
Security-59754.80.3.tar.gz
[apple/security.git] / keychain / ckks / CKKSKeychainView.m
index 0f7416717931d2855d71e785cd755ffb1f1ba110..16be35cfdde300867fa79e2bc002d1ee69ebac52 100644 (file)
@@ -32,7 +32,8 @@
 #endif
 
 #import "CKKS.h"
-#import "CKKSAPSReceiver.h"
+#import "keychain/ckks/CKKSStates.h"
+#import "OctagonAPSReceiver.h"
 #import "CKKSIncomingQueueEntry.h"
 #import "CKKSOutgoingQueueEntry.h"
 #import "CKKSCurrentKeyPointer.h"
@@ -43,9 +44,8 @@
 #import "CKKSIncomingQueueOperation.h"
 #import "CKKSNewTLKOperation.h"
 #import "CKKSProcessReceivedKeysOperation.h"
-#import "CKKSZone.h"
 #import "CKKSFetchAllRecordZoneChangesOperation.h"
-#import "CKKSHealKeyHierarchyOperation.h"
+#import "keychain/ckks/CKKSHealKeyHierarchyOperation.h"
 #import "CKKSReencryptOutgoingItemsOperation.h"
 #import "CKKSScanLocalItemsOperation.h"
 #import "CKKSSynchronizeOperation.h"
 #import "CKKSManifest.h"
 #import "CKKSManifestLeafRecord.h"
 #import "CKKSZoneChangeFetcher.h"
+#import "CKKSAnalytics.h"
+#import "keychain/analytics/CKKSLaunchSequence.h"
+#import "keychain/ckks/CKKSCloudKitClassDependencies.h"
 #import "keychain/ckks/CKKSDeviceStateEntry.h"
 #import "keychain/ckks/CKKSNearFutureScheduler.h"
 #import "keychain/ckks/CKKSCurrentItemPointer.h"
+#import "keychain/ckks/CKKSCreateCKZoneOperation.h"
+#import "keychain/ckks/CKKSDeleteCKZoneOperation.h"
 #import "keychain/ckks/CKKSUpdateCurrentItemPointerOperation.h"
 #import "keychain/ckks/CKKSUpdateDeviceStateOperation.h"
-#import "keychain/ckks/CKKSLockStateTracker.h"
 #import "keychain/ckks/CKKSNotifier.h"
 #import "keychain/ckks/CloudKitCategories.h"
+#import "keychain/ckks/CKKSTLKShareRecord.h"
+#import "keychain/ckks/CKKSHealTLKSharesOperation.h"
+#import "keychain/ckks/CKKSLocalSynchronizeOperation.h"
+#import "keychain/ckks/CKKSPeerProvider.h"
+#import "keychain/ckks/CKKSCheckKeyHierarchyOperation.h"
+#import "keychain/ckks/CKKSViewManager.h"
+#import "keychain/categories/NSError+UsefulConstructors.h"
+
+#import "keychain/ckks/CKKSLocalResetOperation.h"
+
+#import "keychain/ot/OTConstants.h"
+#import "keychain/ot/OTDefines.h"
+#import "keychain/ot/OctagonCKKSPeerAdapter.h"
+#import "keychain/ot/ObjCImprovements.h"
 
 #include <utilities/SecCFWrappers.h>
+#include <utilities/SecTrace.h>
 #include <utilities/SecDb.h>
-#include <securityd/SecDbItem.h>
-#include <securityd/SecItemDb.h>
-#include <securityd/SecItemSchema.h>
-#include <securityd/SecItemServer.h>
-#include <utilities/debugging.h>
+#include "keychain/securityd/SecDbItem.h"
+#include "keychain/securityd/SecItemDb.h"
+#include "keychain/securityd/SecItemSchema.h"
+#include "keychain/securityd/SecItemServer.h"
 #include <Security/SecItemPriv.h>
-#include <Security/SecureObjectSync/SOSAccountTransaction.h>
-#include <utilities/SecADWrapper.h>
+#include "keychain/SecureObjectSync/SOSAccountTransaction.h"
 #include <utilities/SecPLWrappers.h>
+#include <os/transaction_private.h>
+
+#import "keychain/trust/TrustedPeers/TPSyncingPolicy.h"
+#import <Security/SecItemInternal.h>
 
 #if OCTAGON
+
 @interface CKKSKeychainView()
-@property bool setupSuccessful;
-@property bool keyStateFetchRequested;
-@property bool keyStateFullRefetchRequested;
-@property bool keyStateProcessRequested;
-@property (atomic) NSString *activeTLK;
 
 @property (readonly) Class<CKKSNotifier> notifierClass;
 
-@property CKKSNearFutureScheduler* initializeScheduler;
+// Slows down all outgoing queue operations
+@property CKKSNearFutureScheduler* outgoingQueueOperationScheduler;
 
 @property CKKSResultOperation* processIncomingQueueAfterNextUnlockOperation;
+@property CKKSResultOperation* resultsOfNextIncomingQueueOperationOperation;
+
+// An extra queue for semaphore-waiting-based NSOperations
+@property NSOperationQueue* waitingQueue;
+
+// Scratch space for resyncs
+@property (nullable) NSMutableSet<NSString*>* resyncRecordsSeen;
+
+
 
-@property NSMutableDictionary<NSString*, SecBoolNSErrorCallback>* pendingSyncCallbacks;
+@property NSOperationQueue* operationQueue;
+@property CKKSResultOperation* accountLoggedInDependency;
+@property BOOL halted;
+
+// Make these readwrite
+@property NSArray<CKKSPeerProviderState*>* currentTrustStates;
+
+@property NSMutableSet<CKKSFetchBecause*>* currentFetchReasons;
 @end
 #endif
 
 @implementation CKKSKeychainView
 #if OCTAGON
 
-- (instancetype)initWithContainer:     (CKContainer*) container
-                             zoneName: (NSString*) zoneName
-                       accountTracker:(CKKSCKAccountStateTracker*) accountTracker
-                     lockStateTracker:(CKKSLockStateTracker*) lockStateTracker
-                     savedTLKNotifier:(CKKSNearFutureScheduler*) savedTLKNotifier
- fetchRecordZoneChangesOperationClass: (Class<CKKSFetchRecordZoneChangesOperation>) fetchRecordZoneChangesOperationClass
-    modifySubscriptionsOperationClass: (Class<CKKSModifySubscriptionsOperation>) modifySubscriptionsOperationClass
-      modifyRecordZonesOperationClass: (Class<CKKSModifyRecordZonesOperation>) modifyRecordZonesOperationClass
-                   apsConnectionClass: (Class<CKKSAPSConnection>) apsConnectionClass
-                        notifierClass: (Class<CKKSNotifier>) notifierClass
+- (instancetype)initWithContainer:(CKContainer*)container
+                         zoneName:(NSString*)zoneName
+                   accountTracker:(CKKSAccountStateTracker*)accountTracker
+                 lockStateTracker:(CKKSLockStateTracker*)lockStateTracker
+              reachabilityTracker:(CKKSReachabilityTracker*)reachabilityTracker
+                    changeFetcher:(CKKSZoneChangeFetcher*)fetcher
+                     zoneModifier:(CKKSZoneModifier*)zoneModifier
+                 savedTLKNotifier:(CKKSNearFutureScheduler*)savedTLKNotifier
+        cloudKitClassDependencies:(CKKSCloudKitClassDependencies*)cloudKitClassDependencies
 {
 
-    if(self = [super initWithContainer:container
-                              zoneName:zoneName
-                        accountTracker:accountTracker
-  fetchRecordZoneChangesOperationClass:fetchRecordZoneChangesOperationClass
-     modifySubscriptionsOperationClass:modifySubscriptionsOperationClass
-       modifyRecordZonesOperationClass:modifyRecordZonesOperationClass
-                    apsConnectionClass:apsConnectionClass]) {
-        __weak __typeof(self) weakSelf = self;
+    if((self = [super init])) {
+        WEAKIFY(self);
+
+        _container = container;
+        _zoneName = zoneName;
+        _accountTracker = accountTracker;
+        _reachabilityTracker = reachabilityTracker;
+        _cloudKitClassDependencies = cloudKitClassDependencies;
+
+        _halted = NO;
+
+        _database = [_container privateCloudDatabase];
+        _zoneID = [[CKRecordZoneID alloc] initWithZoneName:zoneName ownerName:CKCurrentUserDefaultName];
+
+        _accountStatus = CKKSAccountStatusUnknown;
+        _accountLoggedInDependency = [self createAccountLoggedInDependency:@"CloudKit account logged in."];
+
+        _queue = dispatch_queue_create([[NSString stringWithFormat:@"CKKSQueue.%@.zone.%@", container.containerIdentifier, zoneName] UTF8String], DISPATCH_QUEUE_SERIAL_WITH_AUTORELEASE_POOL);
+        _operationQueue = [[NSOperationQueue alloc] init];
+
+
+        _loggedIn = [[CKKSCondition alloc] init];
+        _loggedOut = [[CKKSCondition alloc] init];
+        _accountStateKnown = [[CKKSCondition alloc] init];
+
+        _initiatedLocalScan = NO;
+
+        _trustStatus = CKKSAccountStatusUnknown;
 
         _incomingQueueOperations = [NSHashTable weakObjectsHashTable];
         _outgoingQueueOperations = [NSHashTable weakObjectsHashTable];
-        _zoneChangeFetcher = [[CKKSZoneChangeFetcher alloc] initWithCKKSKeychainView: self];
+        _scanLocalItemsOperations = [NSHashTable weakObjectsHashTable];
+
+        _currentTrustStates = @[];
+
+        _currentFetchReasons = [NSMutableSet set];
+
+        _launch = [[CKKSLaunchSequence alloc] initWithRocketName:@"com.apple.security.ckks.launch"];
+        [_launch addAttribute:@"view" value:zoneName];
+
+        _zoneChangeFetcher = fetcher;
+        [fetcher registerClient:self];
+
+        _resyncRecordsSeen = nil;
 
-        _notifierClass = notifierClass;
+        _notifierClass = cloudKitClassDependencies.notifierClass;
         _notifyViewChangedScheduler = [[CKKSNearFutureScheduler alloc] initWithName:[NSString stringWithFormat: @"%@-notify-scheduler", self.zoneName]
                                                             initialDelay:250*NSEC_PER_MSEC
                                                          continuingDelay:1*NSEC_PER_SEC
                                                         keepProcessAlive:true
+                                                          dependencyDescriptionCode:CKKSResultDescriptionPendingViewChangedScheduling
                                                                    block:^{
-                                                                       __strong __typeof(self) strongSelf = weakSelf;
-                                                                       ckksnotice("ckks", strongSelf, "");
-                                                                       [strongSelf.notifierClass post:[NSString stringWithFormat:@"com.apple.security.view-change.%@", strongSelf.zoneName]];
+                                                                       STRONGIFY(self);
+                                                                       [self.notifierClass post:[NSString stringWithFormat:@"com.apple.security.view-change.%@", self.zoneName]];
+                                                                       [self.notifierClass post:[NSString stringWithUTF8String:kSecServerKeychainChangedNotification]];
+
 
                                                                        // Ugly, but: the Manatee and Engram views need to send a fake 'PCS' view change.
                                                                        // TODO: make this data-driven somehow
-                                                                       if([strongSelf.zoneName isEqualToString:@"Manatee"] || [strongSelf.zoneName isEqualToString:@"Engram"]) {
-                                                                           [strongSelf.notifierClass post:@"com.apple.security.view-change.PCS"];
+                                                                       if([self.zoneName isEqualToString:@"Manatee"] ||
+                                                                          [self.zoneName isEqualToString:@"Engram"] ||
+                                                                          [self.zoneName isEqualToString:@"ApplePay"] ||
+                                                                          [self.zoneName isEqualToString:@"Home"] ||
+                                                                          [self.zoneName isEqualToString:@"LimitedPeersAllowed"]) {
+                                                                           [self.notifierClass post:@"com.apple.security.view-change.PCS"];
                                                                        }
                                                                    }];
 
-        _pendingSyncCallbacks = [[NSMutableDictionary alloc] init];
-
-        _lockStateTracker = lockStateTracker;
-        _savedTLKNotifier = savedTLKNotifier;
-
-        _setupSuccessful = false;
+        _notifyViewReadyScheduler = [[CKKSNearFutureScheduler alloc] initWithName:[NSString stringWithFormat: @"%@-ready-scheduler", self.zoneName]
+                                                                       initialDelay:250*NSEC_PER_MSEC
+                                                                    continuingDelay:120*NSEC_PER_SEC
+                                                                   keepProcessAlive:true
+                                                          dependencyDescriptionCode:CKKSResultDescriptionPendingViewChangedScheduling
+                                                                              block:^{
+                                                                                  STRONGIFY(self);
+                                                                                  NSDistributedNotificationCenter *center = [self.cloudKitClassDependencies.nsdistributednotificationCenterClass defaultCenter];
 
-        _keyHierarchyConditions = [[NSMutableDictionary alloc] init];
-        [CKKSZoneKeyStateMap() enumerateKeysAndObjectsUsingBlock:^(CKKSZoneKeyState * _Nonnull key, NSNumber * _Nonnull obj, BOOL * _Nonnull stop) {
-            [self.keyHierarchyConditions setObject: [[CKKSCondition alloc] init] forKey:key];
-        }];
-
-        self.keyHierarchyState = SecCKKSZoneKeyStateInitializing;
-        _keyHierarchyError = nil;
-        _keyHierarchyOperationGroup = nil;
-        _keyStateMachineOperation = nil;
-        _keyStateFetchRequested = false;
-        _keyStateProcessRequested = false;
+                                                                                  [center postNotificationName:@"com.apple.security.view-become-ready"
+                                                                                                        object:nil
+                                                                                                      userInfo:@{ @"view" : self.zoneName ?: @"unknown" }
+                                                                                                       options:0];
+                                                                              }];
 
-        _keyStateReadyDependency = [CKKSResultOperation operationWithBlock:^{
-            ckksnotice("ckkskey", weakSelf, "Key state has become ready for the first time.");
-        }];
-        self.keyStateReadyDependency.name = [NSString stringWithFormat: @"%@-key-state-ready", self.zoneName];
 
-        dispatch_time_t initializeDelay = SecCKKSTestsEnabled() ? NSEC_PER_MSEC * 500 : NSEC_PER_SEC * 30;
-        _initializeScheduler = [[CKKSNearFutureScheduler alloc] initWithName:[NSString stringWithFormat: @"%@-zone-initializer", self.zoneName]
-                                                                initialDelay:0
-                                                             continuingDelay:initializeDelay
-                                                            keepProcessAlive:false
-                                                                       block:^{
-                                                                           __strong __typeof(self) strongSelf = weakSelf;
-                                                                           ckksnotice("ckks", strongSelf, "initialize-scheduler restarting setup");
-                                                                           [strongSelf maybeRestartSetup];
-                                                                       }];
+        _lockStateTracker = lockStateTracker;
 
+        _stateMachine = [[OctagonStateMachine alloc] initWithName:[NSString stringWithFormat:@"ckks-%@", self.zoneName]
+                                                           states:[NSSet setWithArray:[CKKSZoneKeyStateMap() allKeys]]
+                                                            flags:CKKSAllStateFlags()
+                                                     initialState:SecCKKSZoneKeyStateWaitForCloudKitAccountStatus
+                                                            queue:self.queue
+                                                      stateEngine:self
+                                                 lockStateTracker:lockStateTracker];
+        [_stateMachine startOperation];
+
+        _waitingQueue = [[NSOperationQueue alloc] init];
+        _waitingQueue.maxConcurrentOperationCount = 5;
+
+        _keyStateReadyDependency = [self createKeyStateReadyDependency: @"Key state has become ready for the first time."];
+
+        dispatch_time_t initialOutgoingQueueDelay = SecCKKSReduceRateLimiting() ? NSEC_PER_MSEC * 200 : NSEC_PER_SEC * 1;
+        dispatch_time_t continuingOutgoingQueueDelay = SecCKKSReduceRateLimiting() ? NSEC_PER_MSEC * 200 : NSEC_PER_SEC * 30;
+        _outgoingQueueOperationScheduler = [[CKKSNearFutureScheduler alloc] initWithName:[NSString stringWithFormat: @"%@-outgoing-queue-scheduler", self.zoneName]
+                                                                            initialDelay:initialOutgoingQueueDelay
+                                                                         continuingDelay:continuingOutgoingQueueDelay
+                                                                        keepProcessAlive:false
+                                                               dependencyDescriptionCode:CKKSResultDescriptionPendingOutgoingQueueScheduling
+                                                                                   block:^{}];
+
+        _operationDependencies = [[CKKSOperationDependencies alloc] initWithZoneID:self.zoneID
+                                                                      zoneModifier:zoneModifier
+                                                                  ckoperationGroup:nil
+                                                                       flagHandler:_stateMachine
+                                                                    launchSequence:_launch
+                                                                  lockStateTracker:_lockStateTracker
+                                                               reachabilityTracker:reachabilityTracker
+                                                                     peerProviders:@[]
+                                                                  databaseProvider:self
+                                                        notifyViewChangedScheduler:_notifyViewChangedScheduler
+                                                                  savedTLKNotifier:savedTLKNotifier];
     }
     return self;
 }
 
 - (NSString*)description {
-    return [NSString stringWithFormat:@"<%@: %@>", NSStringFromClass([self class]), self.zoneName];
+    return [NSString stringWithFormat:@"<%@: %@ (%@)>", NSStringFromClass([self class]), self.zoneName, self.keyHierarchyState];
 }
 
 - (NSString*)debugDescription {
-    return [NSString stringWithFormat:@"<%@: %@ %p>", NSStringFromClass([self class]), self.zoneName, self];
+    return [NSString stringWithFormat:@"<%@: %@ (%@) %p>", NSStringFromClass([self class]), self.zoneName, self.keyHierarchyState, self];
 }
 
 - (CKKSZoneKeyState*)keyHierarchyState {
-    return _keyHierarchyState;
+    return self.stateMachine.currentState;
 }
 
-- (void)setKeyHierarchyState:(CKKSZoneKeyState *)keyHierarchyState {
-    if((keyHierarchyState == nil && _keyHierarchyState == nil) || ([keyHierarchyState isEqualToString:_keyHierarchyState])) {
-        // No change, do nothing.
-    } else {
-        // Fixup the condition variables
-        if(_keyHierarchyState) {
-            self.keyHierarchyConditions[_keyHierarchyState] = [[CKKSCondition alloc] init];
-        }
-        if(keyHierarchyState) {
-            [self.keyHierarchyConditions[keyHierarchyState] fulfill];
-        }
-    }
-
-    _keyHierarchyState = keyHierarchyState;
-}
-
-- (NSString *)lastActiveTLKUUID
+- (NSMutableDictionary<CKKSZoneKeyState*, CKKSCondition*>*)keyHierarchyConditions
 {
-    return self.activeTLK;
-}
-
-- (void) initializeZone {
-    // Unfortunate, but makes retriggering easy.
-    [self.initializeScheduler trigger];
-}
-
-- (void)maybeRestartSetup {
-    [self dispatchSync: ^bool{
-        if(self.setupStarted && !self.setupComplete) {
-            ckksdebug("ckks", self, "setup has restarted. Ignoring timer fire");
-            return false;
-        }
-
-        if(self.setupSuccessful) {
-            ckksdebug("ckks", self, "setup has completed successfully. Ignoring timer fire");
-            return false;
-        }
-
-        [self resetSetup];
-        [self _onqueueInitializeZone];
-        return true;
-    }];
-}
-
-- (void)resetSetup {
-    [super resetSetup];
-    self.setupSuccessful = false;
-
-    // Key hierarchy state machine resets, too
-    self.keyHierarchyState = SecCKKSZoneKeyStateInitializing;
-    _keyHierarchyError = nil;
+    return self.stateMachine.stateConditions;
 }
 
- - (void)_onqueueInitializeZone {
-    if(!SecCKKSIsEnabled()) {
-        ckksnotice("ckks", self, "Skipping CloudKit initialization due to disabled CKKS");
-        return;
+- (void)ensureKeyStateReadyDependency:(NSString*)resetMessage {
+    NSOperation* oldKSRD = self.keyStateReadyDependency;
+    self.keyStateReadyDependency = [self createKeyStateReadyDependency:resetMessage];
+    if(oldKSRD) {
+        [oldKSRD addDependency:self.keyStateReadyDependency];
+        [self.waitingQueue addOperation:oldKSRD];
     }
+}
 
-    dispatch_assert_queue(self.queue);
-
-    __weak __typeof(self) weakSelf = self;
-
-    NSBlockOperation* afterZoneSetup = [NSBlockOperation blockOperationWithBlock: ^{
-        __strong __typeof(weakSelf) strongSelf = weakSelf;
-        if(!strongSelf) {
-            ckkserror("ckks", strongSelf, "received callback for released object");
-            return;
-        }
-
-        __block bool quit = false;
-
-        [strongSelf dispatchSync: ^bool {
-            ckksnotice("ckks", strongSelf, "Zone setup progress: %@ %d %@ %d %@",
-                       [CKKSCKAccountStateTracker stringFromAccountStatus:strongSelf.accountStatus],
-                       strongSelf.zoneCreated, strongSelf.zoneCreatedError, strongSelf.zoneSubscribed, strongSelf.zoneSubscribedError);
-
-            NSError* error = nil;
-            CKKSZoneStateEntry* ckse = [CKKSZoneStateEntry state: strongSelf.zoneName];
-            ckse.ckzonecreated = strongSelf.zoneCreated;
-            ckse.ckzonesubscribed = strongSelf.zoneSubscribed;
-
-            // Although, if the zone subscribed error says there's no zone, mark down that there's no zone
-            if(strongSelf.zoneSubscribedError &&
-               [strongSelf.zoneSubscribedError.domain isEqualToString:CKErrorDomain] && strongSelf.zoneSubscribedError.code == CKErrorPartialFailure) {
-                NSError* subscriptionError = strongSelf.zoneSubscribedError.userInfo[CKPartialErrorsByItemIDKey][strongSelf.zoneID];
-                if(subscriptionError && [subscriptionError.domain isEqualToString:CKErrorDomain] && subscriptionError.code == CKErrorZoneNotFound) {
-
-                    ckkserror("ckks", strongSelf, "zone subscription error appears to say the zone doesn't exist, fixing status: %@", strongSelf.zoneSubscribedError);
-                    ckse.ckzonecreated = false;
-                }
-            }
-
-            [ckse saveToDatabase: &error];
-            if(error) {
-                ckkserror("ckks", strongSelf, "couldn't save zone creation status for %@: %@", strongSelf.zoneName, error);
-            }
-
-            if(!strongSelf.zoneCreated || !strongSelf.zoneSubscribed || strongSelf.accountStatus != CKAccountStatusAvailable) {
-                // Something has gone very wrong. Error out and maybe retry.
-                quit = true;
-
-                // Note that CKKSZone has probably called [handleLogout]; which means we have a key hierarchy reset queued up. Error here anyway.
-                NSError* realReason = strongSelf.zoneCreatedError ? strongSelf.zoneCreatedError : strongSelf.zoneSubscribedError;
-                [strongSelf _onqueueAdvanceKeyStateMachineToState: SecCKKSZoneKeyStateError withError: realReason];
-
-                // We're supposed to be up, but something has gone wrong. Blindly retry until it works.
-                if(strongSelf.accountStatus == CKKSAccountStatusAvailable) {
-                    [strongSelf.initializeScheduler trigger];
-                    ckksnotice("ckks", strongSelf, "We're logged in, but setup didn't work. Scheduling retry for %@", strongSelf.initializeScheduler.nextFireTime);
-                }
-                return true;
-            } else {
-                strongSelf.setupSuccessful = true;
-            }
-
-            return true;
-        }];
-
-        if(quit) {
-            ckkserror("ckks", strongSelf, "Quitting setup.");
-            return;
-        }
-
-        // We can't enter the account queue until an account exists. Before this point, we don't know if one does.
-        [strongSelf dispatchSyncWithAccountQueue: ^bool{
-            CKKSZoneStateEntry* ckse = [CKKSZoneStateEntry state: strongSelf.zoneName];
+- (CKKSResultOperation<OctagonStateTransitionOperationProtocol>*)performInitializedOperation
+{
+    WEAKIFY(self);
+    return [OctagonStateTransitionOperation named:@"ckks-initialized-operation"
+                                        intending:SecCKKSZoneKeyStateBecomeReady
+                                       errorState:SecCKKSZoneKeyStateError
+                              withBlockTakingSelf:^(OctagonStateTransitionOperation * _Nonnull op) {
+        STRONGIFY(self);
+        [self dispatchSyncWithSQLTransaction:^CKKSDatabaseTransactionResult{
+            CKKSOutgoingQueueOperation* outgoingOperation = nil;
+            CKKSIncomingQueueOperation* initialProcess = nil;
+            CKKSScanLocalItemsOperation* initialScan = nil;
+
+            CKKSZoneStateEntry* ckse = [CKKSZoneStateEntry state:self.zoneName];
 
             // Check if we believe we've synced this zone before.
             if(ckse.changeToken == nil) {
-                strongSelf.keyHierarchyOperationGroup = [CKOperationGroup CKKSGroupWithName:@"initial-setup"];
+                self.operationDependencies.ckoperationGroup = [CKOperationGroup CKKSGroupWithName:@"initial-setup"];
 
-                ckksnotice("ckks", strongSelf, "No existing change token; going to try to match local items with CloudKit ones.");
+                ckksnotice("ckks", self, "No existing change token; going to try to match local items with CloudKit ones.");
 
                 // Onboard this keychain: there's likely items in it that we haven't synced yet.
                 // But, there might be items in The Cloud that correspond to these items, with UUIDs that we don't know yet.
                 // First, fetch all remote items.
-                CKKSResultOperation* fetch = [strongSelf.zoneChangeFetcher requestSuccessfulFetch:CKKSFetchBecauseInitialStart];
-                fetch.name = @"initial-fetch";
 
-                // Next, try to process them (replacing local entries)
-                CKKSIncomingQueueOperation* initialProcess = [strongSelf processIncomingQueue: true after: fetch ];
-                initialProcess.name = @"initial-process-incoming-queue";
+                [self.currentFetchReasons addObject:CKKSFetchBecauseInitialStart];
+                op.nextState = SecCKKSZoneKeyStateBeginFetch;
+
+                // Next, try to process them (replacing local entries). This will wait for the key state to be ready.
+                initialProcess = [self processIncomingQueue:true after:nil];
 
                 // If all that succeeds, iterate through all keychain items and find the ones which need to be uploaded
-                strongSelf.initialScanOperation = [[CKKSScanLocalItemsOperation alloc] initWithCKKSKeychainView:strongSelf ckoperationGroup:strongSelf.keyHierarchyOperationGroup];
-                strongSelf.initialScanOperation.name = @"initial-scan-operation";
-                [strongSelf.initialScanOperation addNullableDependency:strongSelf.lockStateTracker.unlockDependency];
-                [strongSelf.initialScanOperation addDependency: initialProcess];
-                [strongSelf scheduleOperation: strongSelf.initialScanOperation];
+                initialScan = [self scanLocalItems:@"initial-scan-operation"
+                                  ckoperationGroup:self.operationDependencies.ckoperationGroup
+                                             after:initialProcess];
 
             } else {
                 // Likely a restart of securityd!
 
-                strongSelf.keyHierarchyOperationGroup = [CKOperationGroup CKKSGroupWithName:@"restart-setup"];
+                // Are there any fixups to run first?
+                self.lastFixupOperation = [CKKSFixups fixup:ckse.lastFixup for:self];
+                if(self.lastFixupOperation) {
+                    ckksnotice("ckksfixup", self, "We have a fixup to perform: %@", self.lastFixupOperation);
+                    [self scheduleOperation:self.lastFixupOperation];
+                    op.nextState = SecCKKSZoneKeyStateWaitForFixupOperation;
+                    return CKKSDatabaseTransactionCommit;
+                }
+
+                // First off, are there any in-flight queue entries? If so, put them back into New.
+                // If they're truly in-flight, we'll "conflict" with ourselves, but that should be fine.
+                NSError* error = nil;
+                [self _onqueueResetAllInflightOQE:&error];
+                if(error) {
+                    ckkserror("ckks", self, "Couldn't reset in-flight OQEs, bad behavior ahead: %@", error);
+                }
 
-                if ([CKKSManifest shouldSyncManifests]) {
-                    strongSelf.egoManifest = [CKKSEgoManifest tryCurrentEgoManifestForZone:strongSelf.zoneName];
+                // Are there any entries waiting for reencryption? If so, set the flag.
+                error = nil;
+                NSArray<CKKSOutgoingQueueEntry*>* reencryptOQEs = [CKKSOutgoingQueueEntry allInState:SecCKKSStateReencrypt
+                                                                                              zoneID:self.zoneID
+                                                                                               error:&error];
+                if(error) {
+                    ckkserror("ckks", self, "Couldn't load reencrypt OQEs, bad behavior ahead: %@", error);
+                }
+                if(reencryptOQEs.count > 0) {
+                    [self.stateMachine _onqueueHandleFlag:CKKSFlagItemReencryptionNeeded];
                 }
 
+                self.operationDependencies.ckoperationGroup = [CKOperationGroup CKKSGroupWithName:@"restart-setup"];
+
                 // If it's been more than 24 hours since the last fetch, fetch and process everything.
+                // Or, if we think we were interrupted in the middle of fetching, fetch some more.
                 // Otherwise, just kick off the local queue processing.
 
                 NSDate* now = [NSDate date];
                 [offset setHour:-24];
                 NSDate* deadline = [[NSCalendar currentCalendar] dateByAddingComponents:offset toDate:now options:0];
 
-                NSOperation* initialProcess = nil;
-                if(ckse.lastFetchTime == nil || [ckse.lastFetchTime compare: deadline] == NSOrderedAscending) {
-                    initialProcess = [strongSelf fetchAndProcessCKChanges:CKKSFetchBecauseSecuritydRestart];
+                if(ckse.lastFetchTime == nil ||
+                   [ckse.lastFetchTime compare: deadline] == NSOrderedAscending ||
+                   ckse.moreRecordsInCloudKit) {
+
+                    op.nextState = SecCKKSZoneKeyStateBeginFetch;
+
                 } else {
-                    initialProcess = [strongSelf processIncomingQueue:false];
+                    // Check if we have an existing key hierarchy in keyset
+                    CKKSCurrentKeySet* keyset = [CKKSCurrentKeySet loadForZone:self.zoneID];
+                    if(keyset.error && !([keyset.error.domain isEqual: @"securityd"] && keyset.error.code == errSecItemNotFound)) {
+                        ckkserror("ckkskey", self, "Error examining existing key hierarchy: %@", keyset.error);
+                    }
+
+                    if(keyset.tlk && keyset.classA && keyset.classC && !keyset.error) {
+                        // This is likely a restart of securityd, and we think we're ready. Double check.
+                        op.nextState = SecCKKSZoneKeyStateBecomeReady;
+
+                    } else {
+                        ckksnotice("ckkskey", self, "No existing key hierarchy for %@. Check if there's one in CloudKit...", self.zoneID.zoneName);
+                        op.nextState = SecCKKSZoneKeyStateBeginFetch;
+                    }
                 }
 
-                if(!strongSelf.egoManifest) {
-                    ckksnotice("ckksmanifest", strongSelf, "No ego manifest on restart; rescanning");
-                    strongSelf.initialScanOperation = [[CKKSScanLocalItemsOperation alloc] initWithCKKSKeychainView:strongSelf ckoperationGroup:strongSelf.keyHierarchyOperationGroup];
-                    strongSelf.initialScanOperation.name = @"initial-scan-operation";
-                    [strongSelf.initialScanOperation addNullableDependency:strongSelf.lockStateTracker.unlockDependency];
-                    [strongSelf.initialScanOperation addDependency: initialProcess];
-                    [strongSelf scheduleOperation: strongSelf.initialScanOperation];
+                if(ckse.lastLocalKeychainScanTime == nil || [ckse.lastLocalKeychainScanTime compare:deadline] == NSOrderedAscending) {
+                    // TODO handle with a state flow
+                    ckksnotice("ckksscan", self, "CKKS scan last occurred at %@; beginning a new one", ckse.lastLocalKeychainScanTime);
+                    initialScan = [self scanLocalItems:ckse.lastLocalKeychainScanTime == nil ? @"initial-scan-operation" : @"24-hr-scan-operation"
+                                      ckoperationGroup:self.operationDependencies.ckoperationGroup
+                                                 after:nil];
                 }
 
-                [strongSelf processOutgoingQueue:strongSelf.keyHierarchyOperationGroup];
+                // Process outgoing queue after re-start
+                outgoingOperation = [self processOutgoingQueueAfter:nil ckoperationGroup:self.operationDependencies.ckoperationGroup];
             }
 
-            // Tell the key state machine to fire off.
-            [strongSelf _onqueueAdvanceKeyStateMachineToState: SecCKKSZoneKeyStateInitialized withError: nil];
-            return true;
+            /*
+             * Launch time is determined by when the zone have:
+             *  1. keystate have become ready
+             *  2. scan local items (if needed)
+             *  3. processed all outgoing item (if needed)
+             * TODO: this should move, once queue processing becomes part of the state machine
+             */
+
+            WEAKIFY(self);
+            NSBlockOperation *seemReady = [NSBlockOperation named:[NSString stringWithFormat:@"seemsReadyForSyncing-%@", self.zoneName] withBlock:^void{
+                STRONGIFY(self);
+                NSError *error = nil;
+                ckksnotice("launch", self, "Launch complete");
+                NSNumber *zoneSize = [CKKSMirrorEntry counts:self.zoneID error:&error];
+                if (zoneSize) {
+                    zoneSize = @(SecBucket1Significant([zoneSize longValue]));
+                    [self.launch addAttribute:@"zonesize" value:zoneSize];
+                }
+                [self.launch launch];
+
+                /*
+                 * Since we think we are ready, signal to CK that its to check for PCS identities again, and create the
+                 * since before we completed this operation, we would probably have failed with a timeout because
+                 * we where busy downloading items from CloudKit and then processing them.
+                 */
+                [self.notifyViewReadyScheduler trigger];
+            }];
+
+            [seemReady addNullableDependency:self.keyStateReadyDependency];
+            [seemReady addNullableDependency:outgoingOperation];
+            [seemReady addNullableDependency:initialScan];
+            [seemReady addNullableDependency:initialProcess];
+            [self scheduleOperation:seemReady];
+
+            return CKKSDatabaseTransactionCommit;
         }];
     }];
-    afterZoneSetup.name = @"view-setup";
+}
 
-    CKKSZoneStateEntry* ckse = [CKKSZoneStateEntry state: self.zoneName];
-    NSOperation* zoneSetupOperation = [self createSetupOperation: ckse.ckzonecreated zoneSubscribed: ckse.ckzonesubscribed];
+- (CKKSResultOperation*)resetLocalData {
+    ckksnotice("ckksreset", self, "Requesting local data reset");
+
+    return [self.stateMachine doWatchedStateMachineRPC:@"ckks-local-reset"
+                                   sourceStates:[NSSet setWithArray:@[
+                                       // TODO: possibly every state?
+                                       SecCKKSZoneKeyStateReady,
+                                       SecCKKSZoneKeyStateWaitForTLK,
+                                       SecCKKSZoneKeyStateWaitForTrust,
+                                       SecCKKSZoneKeyStateWaitForTLKUpload,
+                                       SecCKKSZoneKeyStateLoggedOut,
+                                   ]]
+                                           path:[OctagonStateTransitionPath pathFromDictionary:@{
+                                               SecCKKSZoneKeyStateResettingLocalData: @{
+                                                   SecCKKSZoneKeyStateInitializing: @{
+                                                       SecCKKSZoneKeyStateInitialized: [OctagonStateTransitionPathStep success],
+                                                       SecCKKSZoneKeyStateLoggedOut: [OctagonStateTransitionPathStep success],
+                                                   }
+                                               }
+                                           }]
+                                                 reply:^(NSError * _Nonnull error) {}];
+}
+
+- (CKKSResultOperation*)resetCloudKitZone:(CKOperationGroup*)operationGroup
+{
+    [self.accountStateKnown wait:(SecCKKSTestsEnabled() ? 1*NSEC_PER_SEC : 10*NSEC_PER_SEC)];
 
-    self.viewSetupOperation = [[CKKSGroupOperation alloc] init];
-    self.viewSetupOperation.name = @"view-setup-group";
-    if(!zoneSetupOperation.isFinished) {
-        [self.viewSetupOperation runBeforeGroupFinished: zoneSetupOperation];
-    }
+    // Not overly thread-safe, but a single read is okay
+    if(self.accountStatus != CKKSAccountStatusAvailable) {
+        // No CK account? goodbye!
+        ckksnotice("ckksreset", self, "Requesting reset of CK zone, but no CK account exists");
+        CKKSResultOperation* errorOp = [CKKSResultOperation named:@"fail" withBlockTakingSelf:^(CKKSResultOperation * _Nonnull op) {
+            op.error = [NSError errorWithDomain:CKKSErrorDomain
+                                          code:CKKSNotLoggedIn
+                                   description:@"User is not signed into iCloud."];
+        }];
+
+        [self scheduleOperationWithoutDependencies:errorOp];
+        return errorOp;
+    }
+
+    ckksnotice("ckksreset", self, "Requesting reset of CK zone (logged in)");
+
+    NSDictionary* localResetPath = @{
+        SecCKKSZoneKeyStateInitializing: @{
+            SecCKKSZoneKeyStateInitialized: [OctagonStateTransitionPathStep success],
+            SecCKKSZoneKeyStateLoggedOut: [OctagonStateTransitionPathStep success],
+        },
+    };
+
+    // If the zone delete doesn't work, try it up to two more times
+
+    return [self.stateMachine doWatchedStateMachineRPC:@"ckks-cloud-reset"
+                                          sourceStates:[NSSet setWithArray:@[
+                                              // TODO: possibly every state?
+                                              SecCKKSZoneKeyStateReady,
+                                              SecCKKSZoneKeyStateInitialized,
+                                              SecCKKSZoneKeyStateFetchComplete,
+                                              SecCKKSZoneKeyStateWaitForTLK,
+                                              SecCKKSZoneKeyStateWaitForTrust,
+                                              SecCKKSZoneKeyStateWaitForTLKUpload,
+                                              SecCKKSZoneKeyStateLoggedOut,
+                                          ]]
+                                                  path:[OctagonStateTransitionPath pathFromDictionary:@{
+                                                      SecCKKSZoneKeyStateResettingZone: @{
+                                                          SecCKKSZoneKeyStateResettingLocalData: localResetPath,
+                                                          SecCKKSZoneKeyStateResettingZone: @{
+                                                              SecCKKSZoneKeyStateResettingLocalData: localResetPath,
+                                                              SecCKKSZoneKeyStateResettingZone: @{
+                                                                 SecCKKSZoneKeyStateResettingLocalData: localResetPath,
+                                                              }
+                                                          }
+                                                      }
+                                                  }]
+                                                 reply:^(NSError * _Nonnull error) {}];
+}
 
-    [afterZoneSetup addDependency: zoneSetupOperation];
-    [self.viewSetupOperation runBeforeGroupFinished: afterZoneSetup];
+- (void)keyStateMachineRequestProcess {
+    [self.stateMachine handleFlag:CKKSFlagKeyStateProcessRequested];
+}
+
+- (CKKSResultOperation*)createKeyStateReadyDependency:(NSString*)message {
+    WEAKIFY(self);
+    CKKSResultOperation* keyStateReadyDependency = [CKKSResultOperation operationWithBlock:^{
+        STRONGIFY(self);
+        ckksnotice("ckkskey", self, "CKKS became ready: %@", message);
+    }];
+    keyStateReadyDependency.name = [NSString stringWithFormat: @"%@-key-state-ready", self.zoneName];
+    keyStateReadyDependency.descriptionErrorCode = CKKSResultDescriptionPendingKeyReady;
+    return keyStateReadyDependency;
+}
 
-    [self scheduleAccountStatusOperation: self.viewSetupOperation];
+- (void)_onqueuePokeKeyStateMachine
+{
+    dispatch_assert_queue(self.queue);
+    [self.stateMachine _onqueuePokeStateMachine];
 }
 
-- (bool)_onqueueResetLocalData: (NSError * __autoreleasing *) error {
+- (CKKSResultOperation<OctagonStateTransitionOperationProtocol>* _Nullable)_onqueueNextStateMachineTransition:(OctagonState*)currentState
+                                                                                                        flags:(OctagonFlags*)flags
+                                                                                                 pendingFlags:(id<OctagonStateOnqueuePendingFlagHandler>)pendingFlagHandler
+{
     dispatch_assert_queue(self.queue);
 
-    NSError* localerror = nil;
-    bool setError = false; // Ugly, but this is the only way to return the first error given
+    // Resetting back to 'loggedout' takes all precedence.
+    if([flags _onqueueContains:CKKSFlagCloudKitLoggedOut]) {
+        [flags _onqueueRemoveFlag:CKKSFlagCloudKitLoggedOut];
+        ckksnotice("ckkskey", self, "CK account is not present");
 
-    CKKSZoneStateEntry* ckse = [CKKSZoneStateEntry state: self.zoneName];
-    ckse.ckzonecreated = false;
-    ckse.ckzonesubscribed = false; // I'm actually not sure about this: can you be subscribed to a non-existent zone?
-    ckse.changeToken = NULL;
-    [ckse saveToDatabase: &localerror];
-    if(localerror) {
-        ckkserror("ckks", self, "couldn't reset zone status for %@: %@", self.zoneName, localerror);
-        if(error && !setError) {
-            *error = localerror; setError = true;
-        }
+        [self ensureKeyStateReadyDependency:@"cloudkit-account-not-present"];
+        return [[CKKSLocalResetOperation alloc] initWithDependencies:self.operationDependencies
+                                                       intendedState:SecCKKSZoneKeyStateLoggedOut
+                                                          errorState:SecCKKSZoneKeyStateError];
     }
 
-    [CKKSMirrorEntry deleteAll:self.zoneID error: &localerror];
-    if(localerror) {
-        ckkserror("ckks", self, "couldn't delete all CKKSMirrorEntry: %@", localerror);
-        if(error && !setError) {
-            *error = localerror; setError = true;
-        }
+    if([flags _onqueueContains:CKKSFlagCloudKitZoneMissing]) {
+        [flags _onqueueRemoveFlag:CKKSFlagCloudKitZoneMissing];
+
+        [self ensureKeyStateReadyDependency:@"cloudkit-zone-missing"];
+        // The zone is gone! Let's reset our local state, which will feed into recreating the zone
+        return [OctagonStateTransitionOperation named:@"ck-zone-missing"
+                                             entering:SecCKKSZoneKeyStateResettingLocalData];
     }
 
-    [CKKSOutgoingQueueEntry deleteAll:self.zoneID error: &localerror];
-    if(localerror) {
-        ckkserror("ckks", self, "couldn't delete all CKKSOutgoingQueueEntry: %@", localerror);
-        if(error && !setError) {
-            *error = localerror; setError = true;
-        }
+    if([flags _onqueueContains:CKKSFlagChangeTokenExpired]) {
+        [flags _onqueueRemoveFlag:CKKSFlagChangeTokenExpired];
+
+        [self ensureKeyStateReadyDependency:@"cloudkit-change-token-expired"];
+        // Our change token is invalid! We'll have to refetch the world, so let's delete everything locally.
+        return [OctagonStateTransitionOperation named:@"ck-token-expired"
+                                             entering:SecCKKSZoneKeyStateResettingLocalData];
     }
 
-    [CKKSIncomingQueueEntry deleteAll:self.zoneID error: &localerror];
-    if(localerror) {
-        ckkserror("ckks", self, "couldn't delete all CKKSIncomingQueueEntry: %@", localerror);
-        if(error && !setError) {
-            *error = localerror; setError = true;
+    if([currentState isEqualToString:SecCKKSZoneKeyStateLoggedOut]) {
+        if([flags _onqueueContains:CKKSFlagCloudKitLoggedIn] || self.accountStatus == CKKSAccountStatusAvailable) {
+            [flags _onqueueRemoveFlag:CKKSFlagCloudKitLoggedIn];
+
+            ckksnotice("ckkskey", self, "CloudKit account now present");
+            return [OctagonStateTransitionOperation named:@"ck-sign-in"
+                                                 entering:SecCKKSZoneKeyStateInitializing];
         }
-    }
 
-    [CKKSKey deleteAll:self.zoneID error: &localerror];
-    if(localerror) {
-        ckkserror("ckks", self, "couldn't delete all CKKSKey: %@", localerror);
-        if(error && !setError) {
-            *error = localerror; setError = true;
+        if([flags _onqueueContains:CKKSFlag24hrNotification]) {
+            [flags _onqueueRemoveFlag:CKKSFlag24hrNotification];
         }
+        return nil;
     }
 
-    [CKKSCurrentKeyPointer deleteAll:self.zoneID error: &localerror];
-    if(localerror) {
-        ckkserror("ckks", self, "couldn't delete all CKKSCurrentKeyPointer: %@", localerror);
-        if(error && !setError) {
-            *error = localerror; setError = true;
+    if([currentState isEqualToString: SecCKKSZoneKeyStateWaitForCloudKitAccountStatus]) {
+        if([flags _onqueueContains:CKKSFlagCloudKitLoggedIn] || self.accountStatus == CKKSAccountStatusAvailable) {
+            [flags _onqueueRemoveFlag:CKKSFlagCloudKitLoggedIn];
+
+            ckksnotice("ckkskey", self, "CloudKit account now present");
+            return [OctagonStateTransitionOperation named:@"ck-sign-in"
+                                                 entering:SecCKKSZoneKeyStateInitializing];
         }
-    }
 
-    [CKKSDeviceStateEntry deleteAll:self.zoneID error:&localerror];
-    if(localerror) {
-        ckkserror("ckks", self, "couldn't delete all CKKSDeviceStateEntry: %@", localerror);
-        if(error && !setError) {
-            *error = localerror; setError = true;
+        if([flags _onqueueContains:CKKSFlagCloudKitLoggedOut]) {
+            [flags _onqueueRemoveFlag:CKKSFlagCloudKitLoggedOut];
+            ckksnotice("ckkskey", self, "No account available");
+
+            return [[CKKSLocalResetOperation alloc] initWithDependencies:self.operationDependencies
+                                                           intendedState:SecCKKSZoneKeyStateLoggedOut
+                                                              errorState:SecCKKSZoneKeyStateError];
         }
+        return nil;
     }
 
-    [self _onqueueAdvanceKeyStateMachineToState: SecCKKSZoneKeyStateInitializing withError:nil];
+    [self.launch addEvent:currentState];
 
-    return (localerror == nil && !setError);
-}
+    if([currentState isEqual:SecCKKSZoneKeyStateInitializing]) {
+        if(self.accountStatus == CKKSAccountStatusNoAccount) {
+            ckksnotice("ckkskey", self, "CloudKit account is missing. Departing!");
+            return [[CKKSLocalResetOperation alloc] initWithDependencies:self.operationDependencies
+                                                           intendedState:SecCKKSZoneKeyStateLoggedOut
+                                                              errorState:SecCKKSZoneKeyStateError];
+        }
 
-- (CKKSResultOperation*)resetLocalData {
-    __weak __typeof(self) weakSelf = self;
+        // Begin zone creation, but rate-limit it
+        CKKSCreateCKZoneOperation* pendingInitializeOp = [[CKKSCreateCKZoneOperation alloc] initWithDependencies:self.operationDependencies
+                                                                                                   intendedState:SecCKKSZoneKeyStateInitialized
+                                                                                                      errorState:SecCKKSZoneKeyStateZoneCreationFailed];
+        [pendingInitializeOp addNullableDependency:self.operationDependencies.zoneModifier.cloudkitRetryAfter.operationDependency];
+        [self.operationDependencies.zoneModifier.cloudkitRetryAfter trigger];
+
+        return pendingInitializeOp;
+    }
 
-    CKKSGroupOperation* resetFollowUp = [[CKKSGroupOperation alloc] init];
-    resetFollowUp.name = @"local-reset-follow-up-group";
-    __weak __typeof(resetFollowUp) weakResetFollowUp = resetFollowUp;
+    if([currentState isEqualToString:SecCKKSZoneKeyStateWaitForFixupOperation]) {
+        // TODO: fixup operations should become part of the state machine
+        ckksnotice("ckkskey", self, "Waiting for the fixup operation: %@", self.lastFixupOperation);
+        OctagonStateTransitionOperation* op = [OctagonStateTransitionOperation named:@"wait-for-fixup" entering:SecCKKSZoneKeyStateInitialized];
+        [op addNullableDependency:self.lastFixupOperation];
+        return op;
+    }
 
-    CKKSResultOperation* op = [[CKKSResultOperation alloc] init];
-    op.name = @"local-reset";
+    if([currentState isEqualToString:SecCKKSZoneKeyStateInitialized]) {
+        // We're initialized and CloudKit is ready. If we're trusted, see what needs done. Otherwise, wait.
+        return [self performInitializedOperation];
+    }
 
-    __weak __typeof(op) weakOp = op;
-    [op addExecutionBlock:^{
-        __strong __typeof(self) strongSelf = weakSelf;
-        __strong __typeof(op) strongOp = weakOp;
-        __strong __typeof(resetFollowUp) strongResetFollowUp = weakResetFollowUp;
-        if(!strongSelf || !strongOp || !strongResetFollowUp) {
-            return;
+    // In error? You probably aren't getting out.
+    if([currentState isEqualToString:SecCKKSZoneKeyStateError]) {
+        if([flags _onqueueContains:CKKSFlagCloudKitLoggedIn]) {
+            [flags _onqueueRemoveFlag:CKKSFlagCloudKitLoggedIn];
+
+            // Worth one last shot. Reset everything locally, and try again.
+            return [[CKKSLocalResetOperation alloc] initWithDependencies:self.operationDependencies
+                                                           intendedState:SecCKKSZoneKeyStateInitializing
+                                                              errorState:SecCKKSZoneKeyStateError];
         }
 
-        __block NSError* error = nil;
+        ckkserror("ckkskey", self, "Staying in error state %@", currentState);
+        return nil;
+    }
 
-        [strongSelf dispatchSync: ^bool{
-            [self _onqueueResetLocalData: &error];
-            return true;
-        }];
+    if([currentState isEqualToString:SecCKKSZoneKeyStateResettingZone]) {
+        ckksnotice("ckkskey", self, "Deleting the CloudKit Zone");
 
-        [strongSelf resetSetup];
+        [self ensureKeyStateReadyDependency:@"ck-zone-reset"];
+        return [[CKKSDeleteCKZoneOperation alloc] initWithDependencies:self.operationDependencies
+                                                         intendedState:SecCKKSZoneKeyStateResettingLocalData
+                                                            errorState:SecCKKSZoneKeyStateResettingZone];
+    }
 
-        if(error) {
-            ckksnotice("ckksreset", strongSelf, "Local reset finished with error %@", error);
-            strongOp.error = error;
-        } else {
-            if(strongSelf.accountStatus == CKKSAccountStatusAvailable) {
-                // Since we're logged in, we expect a reset to fix up the key hierarchy
-                ckksnotice("ckksreset", strongSelf, "logged in; re-initializing zone");
-                [strongSelf initializeZone];
+    if([currentState isEqualToString:SecCKKSZoneKeyStateResettingLocalData]) {
+        ckksnotice("ckkskey", self, "Resetting local data");
 
-                ckksnotice("ckksreset", strongSelf, "waiting for key hierarchy to become ready");
-                CKKSResultOperation* waitOp = [CKKSResultOperation named:@"waiting-for-key-hierarchy" withBlock:^{}];
-                [waitOp timeout: 60*NSEC_PER_SEC];
-                [waitOp addNullableDependency:strongSelf.keyStateReadyDependency];
+        [self ensureKeyStateReadyDependency:@"local-data-reset"];
+        return [[CKKSLocalResetOperation alloc] initWithDependencies:self.operationDependencies
+                                                       intendedState:SecCKKSZoneKeyStateInitializing
+                                                          errorState:SecCKKSZoneKeyStateError];
+    }
 
-                [strongResetFollowUp runBeforeGroupFinished:waitOp];
-            } else {
-                ckksnotice("ckksreset", strongSelf, "logged out; not initializing zone");
-            }
-        }
-    }];
+    if([currentState isEqualToString:SecCKKSZoneKeyStateZoneCreationFailed]) {
+        //Prepare to go back into initializing, as soon as the cloudkitRetryAfter is happy
+        OctagonStateTransitionOperation* op = [OctagonStateTransitionOperation named:@"recover-from-cloudkit-failure" entering:SecCKKSZoneKeyStateInitializing];
 
-    [resetFollowUp runBeforeGroupFinished:op];
-    [self scheduleOperationWithoutDependencies:resetFollowUp];
-    return resetFollowUp;
-}
+        [op addNullableDependency:self.operationDependencies.zoneModifier.cloudkitRetryAfter.operationDependency];
+        [self.operationDependencies.zoneModifier.cloudkitRetryAfter trigger];
 
-- (CKKSResultOperation*)resetCloudKitZone {
-    if(!SecCKKSIsEnabled()) {
-        ckksinfo("ckks", self, "Skipping CloudKit reset due to disabled CKKS");
-        return nil;
+        return op;
     }
 
-    CKKSResultOperation* reset = [super beginResetCloudKitZoneOperation];
+    if([currentState isEqualToString:SecCKKSZoneKeyStateLoseTrust]) {
+        if([flags _onqueueContains:CKKSFlagBeginTrustedOperation]) {
+            [flags _onqueueRemoveFlag:CKKSFlagBeginTrustedOperation];
+            // This was likely a race between some operation and the beginTrustedOperation call! Skip changing state and try again.
+            return [OctagonStateTransitionOperation named:@"begin-trusted-operation" entering:SecCKKSZoneKeyStateInitialized];
+        }
 
-    __weak __typeof(self) weakSelf = self;
-    CKKSGroupOperation* resetFollowUp = [[CKKSGroupOperation alloc] init];
-    resetFollowUp.name = @"cloudkit-reset-follow-up-group";
+        // If our current state is "trusted", fall out
+        if(self.trustStatus == CKKSAccountStatusAvailable) {
+            self.trustStatus = CKKSAccountStatusUnknown;
+        }
+        return [OctagonStateTransitionOperation named:@"trust-loss" entering:SecCKKSZoneKeyStateWaitForTrust];
+    }
 
-    __weak __typeof(resetFollowUp) weakResetFollowUp = resetFollowUp;
-    [resetFollowUp runBeforeGroupFinished: [CKKSResultOperation named:@"cloudkit-reset-follow-up" withBlock: ^{
-        __strong __typeof(weakSelf) strongSelf = weakSelf;
-        if(!strongSelf) {
-            ckkserror("ckks", strongSelf, "received callback for released object");
-            return;
+    if([currentState isEqualToString:SecCKKSZoneKeyStateWaitForTrust]) {
+        if(self.trustStatus == CKKSAccountStatusAvailable) {
+            ckksnotice("ckkskey", self, "Beginning trusted state machine operation");
+            return [OctagonStateTransitionOperation named:@"begin-trusted-operation" entering:SecCKKSZoneKeyStateInitialized];
         }
-        __strong __typeof(resetFollowUp) strongResetFollowUp = weakResetFollowUp;
 
-        if(!reset.error) {
-            ckksnotice("ckks", strongSelf, "Successfully deleted zone %@", strongSelf.zoneName);
-            __block NSError* error = nil;
+        if([flags _onqueueContains:CKKSFlagKeyStateProcessRequested]) {
+            [flags _onqueueRemoveFlag:CKKSFlagKeyStateProcessRequested];
+            return [OctagonStateTransitionOperation named:@"begin-trusted-operation" entering:SecCKKSZoneKeyStateProcess];
+        }
 
-            [strongSelf dispatchSync: ^bool{
-                [strongSelf _onqueueResetLocalData: &error];
-                strongSelf.setupSuccessful = false;
-                return true;
-            }];
+        if([flags _onqueueContains:CKKSFlag24hrNotification]) {
+            [flags _onqueueRemoveFlag:CKKSFlag24hrNotification];
+        }
 
-            if(strongSelf.accountStatus == CKKSAccountStatusAvailable) {
-                // Since we're logged in, we expect a reset to fix up the key hierarchy
-                ckksnotice("ckksreset", strongSelf, "re-initializing zone");
-                [strongSelf initializeZone];
+        return nil;
+    }
 
-                ckksnotice("ckksreset", strongSelf, "waiting for key hierarchy to become ready");
-                CKKSResultOperation* waitOp = [CKKSResultOperation named:@"waiting-for-reset" withBlock:^{}];
-                [waitOp timeout: 60*NSEC_PER_SEC];
-                [waitOp addNullableDependency:strongSelf.keyStateReadyDependency];
+    if([currentState isEqualToString:SecCKKSZoneKeyStateBecomeReady]) {
+        return [[CKKSCheckKeyHierarchyOperation alloc] initWithDependencies:self.operationDependencies
+                                                              intendedState:SecCKKSZoneKeyStateReady
+                                                                 errorState:SecCKKSZoneKeyStateError];
+    }
 
-                [strongResetFollowUp runBeforeGroupFinished:waitOp];
-            } else {
-                ckksnotice("ckksreset", strongSelf, "logged out; not initializing zone");
-            }
-        } else {
-            // Shouldn't ever happen, since reset is a successDependency
-            ckkserror("ckks", strongSelf, "Couldn't reset zone %@: %@", strongSelf.zoneName, reset.error);
+    if([currentState isEqualToString:SecCKKSZoneKeyStateReady]) {
+        // If we're ready, we can ignore the begin trusted flag
+        [flags _onqueueRemoveFlag:CKKSFlagBeginTrustedOperation];
+
+        if(self.keyStateFullRefetchRequested) {
+            // In ready, but something has requested a full refetch.
+            ckksnotice("ckkskey", self, "Kicking off a full key refetch based on request:%d", self.keyStateFullRefetchRequested);
+            [self ensureKeyStateReadyDependency:@"key-state-full-refetch"];
+            return [OctagonStateTransitionOperation named:@"full-refetch" entering:SecCKKSZoneKeyStateNeedFullRefetch];
         }
-    }]];
 
-    [resetFollowUp addSuccessDependency:reset];
-    [self scheduleOperationWithoutDependencies:resetFollowUp];
-    return resetFollowUp;
-}
+        if([flags _onqueueContains:CKKSFlagFetchRequested]) {
+            [flags _onqueueRemoveFlag:CKKSFlagFetchRequested];
+            ckksnotice("ckkskey", self, "Kicking off a key refetch based on request");
+            [self ensureKeyStateReadyDependency:@"key-state-fetch"];
+            return [OctagonStateTransitionOperation named:@"fetch-requested" entering:SecCKKSZoneKeyStateBeginFetch];
+        }
 
-- (void)advanceKeyStateMachine {
-    __weak __typeof(self) weakSelf = self;
+        if([flags _onqueueContains:CKKSFlagKeyStateProcessRequested]) {
+            [flags _onqueueRemoveFlag:CKKSFlagKeyStateProcessRequested];
+            ckksnotice("ckkskey", self, "Kicking off a key reprocess based on request");
+            [self ensureKeyStateReadyDependency:@"key-state-process"];
+            return [OctagonStateTransitionOperation named:@"key-process" entering:SecCKKSZoneKeyStateProcess];
+        }
 
-    [self dispatchAsync: ^bool{
-        __strong __typeof(weakSelf) strongSelf = weakSelf;
-        if(!strongSelf) {
-            ckkserror("ckks", strongSelf, "received callback for released object");
-            false;
+        if(self.trustStatus != CKKSAccountStatusAvailable) {
+            ckksnotice("ckkskey", self, "In ready, but there's no trust; going into waitfortrust");
+            [self ensureKeyStateReadyDependency:@"trust loss"];
+            return [OctagonStateTransitionOperation named:@"trust-gone" entering:SecCKKSZoneKeyStateLoseTrust];
         }
 
-        [strongSelf _onqueueAdvanceKeyStateMachineToState: nil withError: nil];
-        return true;
-    }];
-};
+        if([flags _onqueueContains:CKKSFlagTrustedPeersSetChanged]) {
+            [flags _onqueueRemoveFlag:CKKSFlagTrustedPeersSetChanged];
+            ckksnotice("ckkskey", self, "Received a nudge that the trusted peers set might have changed! Reprocessing.");
+            [self ensureKeyStateReadyDependency:@"Peer set changed"];
+            return [OctagonStateTransitionOperation named:@"trusted-peers-changed" entering:SecCKKSZoneKeyStateProcess];
+        }
 
-- (void)_onqueueKeyStateMachineRequestFetch {
-    dispatch_assert_queue(self.queue);
+        if([flags _onqueueContains:CKKSFlag24hrNotification]) {
+            [flags _onqueueRemoveFlag:CKKSFlag24hrNotification];
 
-    // We're going to set this flag, then nudge the key state machine.
-    // If it was idle, then it should launch a fetch. If there was an active process, this flag will stay high
-    // and the fetch will be launched later.
+            // We'd like to trigger our 24-hr backup fetch and scan.
+            // That's currently part of the Initialized state, so head that way
+            return [OctagonStateTransitionOperation named:@"24-hr-check" entering:SecCKKSZoneKeyStateInitialized];
+        }
 
-    self.keyStateFetchRequested = true;
-    [self _onqueueAdvanceKeyStateMachineToState: nil withError: nil];
-}
+        if([flags _onqueueContains:CKKSFlagItemReencryptionNeeded]) {
+            [flags _onqueueRemoveFlag:CKKSFlagItemReencryptionNeeded];
 
-- (void)_onqueueKeyStateMachineRequestFullRefetch {
-    dispatch_assert_queue(self.queue);
+            // TODO: this should be part of the state machine
+            CKKSReencryptOutgoingItemsOperation* op = [[CKKSReencryptOutgoingItemsOperation alloc] initWithDependencies:self.operationDependencies
+                                                                                                                   ckks:self
+                                                                                                          intendedState:SecCKKSZoneKeyStateReady
+                                                                                                             errorState:SecCKKSZoneKeyStateError];
+            [self scheduleOperation:op];
+            // fall through.
+        }
 
-    self.keyStateFullRefetchRequested = true;
-    [self _onqueueAdvanceKeyStateMachineToState: nil withError: nil];
-}
+        if([flags _onqueueContains:CKKSFlagProcessIncomingQueue]) {
+            [flags _onqueueRemoveFlag:CKKSFlagProcessIncomingQueue];
+            // TODO: this should be part of the state machine
 
-- (void)keyStateMachineRequestProcess {
-    __weak __typeof(self) weakSelf = self;
-    [self dispatchAsync: ^bool{
-        __strong __typeof(weakSelf) strongSelf = weakSelf;
-        if(!strongSelf) {
-            ckkserror("ckks", strongSelf, "received callback for released object");
-            return false;
+            [self processIncomingQueue:true];
+            //return [OctagonStateTransitionOperation named:@"process-outgoing" entering:SecCKKSZoneKeyStateProcessIncomingQueue];
         }
 
-        [strongSelf _onqueueKeyStateMachineRequestProcess];
-        return true;
-    }];
-}
+        if([flags _onqueueContains:CKKSFlagScanLocalItems]) {
+            [flags _onqueueRemoveFlag:CKKSFlagScanLocalItems];
+            ckksnotice("ckkskey", self, "Launching a scan operation to find dropped items");
 
-- (void)_onqueueKeyStateMachineRequestProcess {
-    dispatch_assert_queue(self.queue);
+            // TODO: this should be a state flow
+            [self scanLocalItems:@"per-request"];
+            // fall through
+        }
 
-    // Set the request flag, then nudge the key state machine.
-    // If it was idle, then it should launch a fetch. If there was an active process, this flag will stay high
-    // and the fetch will be launched later.
+        if([flags _onqueueContains:CKKSFlagProcessOutgoingQueue]) {
+            [flags _onqueueRemoveFlag:CKKSFlagProcessOutgoingQueue];
 
-    self.keyStateProcessRequested = true;
-    [self _onqueueAdvanceKeyStateMachineToState: nil withError: nil];
-}
+            [self processOutgoingQueue:nil];
+            // TODO: this should be a state flow.
+            //return [OctagonStateTransitionOperation named:@"process-outgoing" entering:SecCKKSZoneKeyStateProcessOutgoingQueue];
+            // fall through
+        }
 
-// The operations suggested by this state machine should call _onqueueAdvanceKeyStateMachineToState once they are complete.
-// At no other time should keyHierarchyState be modified.
+        // TODO: kick off a key roll if one has been requested
 
-// Note that this function cannot rely on doing any database work; it might get rolled back, especially in an error state
-- (void)_onqueueAdvanceKeyStateMachineToState: (CKKSZoneKeyState*) state withError: (NSError*) error {
-    dispatch_assert_queue(self.queue);
-    __weak __typeof(self) weakSelf = self;
 
-    // Resetting back to 'initializing' takes all precedence.
-    if([state isEqual: SecCKKSZoneKeyStateInitializing]) {
-        ckksnotice("ckkskey", self, "Resetting the key hierarchy state machine back to 'initializing'");
+        // If we reach this point, we're in ready, and will stay there.
+        // Tell the launch and the viewReadyScheduler about that.
 
-        [self.keyStateMachineOperation cancel];
-        self.keyStateMachineOperation = nil;
+        [self.launch launch];
 
-        self.keyHierarchyState = SecCKKSZoneKeyStateInitializing;
-        self.keyHierarchyError = nil;
-        self.keyStateFetchRequested = false;
-        self.keyStateProcessRequested = false;
+        [[CKKSAnalytics logger] setDateProperty:[NSDate date] forKey:CKKSAnalyticsLastKeystateReady zoneName:self.zoneName];
+        if(self.keyStateReadyDependency) {
+            [self scheduleOperation:self.keyStateReadyDependency];
+            self.keyStateReadyDependency = nil;
+        }
 
-        self.keyHierarchyOperationGroup = [CKOperationGroup CKKSGroupWithName:@"key-state-reset"];
-        self.keyStateReadyDependency = [CKKSResultOperation operationWithBlock:^{
-            ckksnotice("ckkskey", weakSelf, "Key state has become ready for the first time (after reset).");
-        }];
-        self.keyStateReadyDependency.name = [NSString stringWithFormat: @"%@-key-state-ready", self.zoneName];
-        return;
+        return nil;
     }
 
-    // Cancels and error states take precedence
-    if([self.keyHierarchyState isEqualToString: SecCKKSZoneKeyStateError] ||
-       [self.keyHierarchyState isEqualToString: SecCKKSZoneKeyStateCancelled] ||
-       self.keyHierarchyError != nil) {
-        // Error state: nowhere to go. Early-exit.
-        ckkserror("ckkskey", self, "Asked to advance state machine from non-exit state %@: %@", self.keyHierarchyState, self.keyHierarchyError);
-        return;
-    }
+    if([currentState isEqualToString:SecCKKSZoneKeyStateReadyPendingUnlock]) {
+        if([flags _onqueueContains:CKKSFlagDeviceUnlocked]) {
+            [flags _onqueueRemoveFlag:CKKSFlagDeviceUnlocked];
+            [self ensureKeyStateReadyDependency:@"Device unlocked"];
+            return [OctagonStateTransitionOperation named:@"key-state-ready-after-unlock" entering:SecCKKSZoneKeyStateBecomeReady];
+        }
 
-    if(error != nil || [state isEqual: SecCKKSZoneKeyStateError]) {
-        // But wait! Is this a "we're locked" error?
-        if([self.lockStateTracker isLockedError:error]) {
-            ckkserror("ckkskey", self, "advised of 'keychain locked' error, ignoring: coming from state (%@): %@", self.keyHierarchyState, error);
-            // After the next unlock, fake that we received the last zone transition
-            CKKSZoneKeyState* lastState = self.keyHierarchyState;
-            self.keyStateMachineOperation = [NSBlockOperation named:@"key-state-after-unlock" withBlock:^{
-                __strong __typeof(self) strongSelf = weakSelf;
-                if(!strongSelf) {
-                    return;
-                }
-                [strongSelf dispatchSync:^bool{
-                    [strongSelf _onqueueAdvanceKeyStateMachineToState:lastState withError:nil];
-                    return true;
-                }];
-            }];
-            state = nil;
-            [self.keyStateMachineOperation addNullableDependency:self.lockStateTracker.unlockDependency];
-            [self scheduleOperation:self.keyStateMachineOperation];
+        if([flags _onqueueContains:CKKSFlagProcessOutgoingQueue]) {
+            [flags _onqueueRemoveFlag:CKKSFlagProcessOutgoingQueue];
+            [self processOutgoingQueue:nil];
+            // TODO: this should become part of the key state hierarchy
+        }
 
-        } else {
-            // Error state: record the error and exit early
-            ckkserror("ckkskey", self, "advised of error: coming from state (%@): %@", self.keyHierarchyState, error);
-            self.keyHierarchyState = SecCKKSZoneKeyStateError;
-            self.keyHierarchyError = error;
-            return;
+        // Ready enough!
+
+        [[CKKSAnalytics logger] setDateProperty:[NSDate date] forKey:CKKSAnalyticsLastKeystateReady zoneName:self.zoneName];
+        if(self.keyStateReadyDependency) {
+            [self scheduleOperation:self.keyStateReadyDependency];
+            self.keyStateReadyDependency = nil;
         }
+
+        OctagonPendingFlag* unlocked = [[OctagonPendingFlag alloc] initWithFlag:CKKSFlagDeviceUnlocked
+                                                                     conditions:OctagonPendingConditionsDeviceUnlocked];
+        [pendingFlagHandler _onqueueHandlePendingFlag:unlocked];
+        return nil;
     }
 
-    if([state isEqual: SecCKKSZoneKeyStateCancelled]) {
-        ckkserror("ckkskey", self, "advised of cancel: coming from state (%@): %@", self.keyHierarchyState, error);
-        self.keyHierarchyState = SecCKKSZoneKeyStateCancelled;
-        self.keyHierarchyError = error;
+    if([currentState isEqualToString:SecCKKSZoneKeyStateBeginFetch]) {
+        ckksnotice("ckkskey", self, "Starting a key hierarchy fetch");
+        [flags _onqueueRemoveFlag:CKKSFlagFetchComplete];
 
-        // Cancel the key ready dependency. Strictly Speaking, this will cause errors down the line, but we're in a cancel state: those operations should be canceled anyway.
-        self.keyHierarchyOperationGroup = nil;
-        [self.keyStateReadyDependency cancel];
-        self.keyStateReadyDependency = nil;
-        return;
-    }
+        WEAKIFY(self);
 
-    // Now that the current or new state isn't an error or a cancel, proceed.
+        NSSet<CKKSFetchBecause*>* fetchReasons = self.currentFetchReasons ?
+            [self.currentFetchReasons setByAddingObject:CKKSFetchBecauseKeyHierarchy] :
+            [NSSet setWithObject:CKKSFetchBecauseKeyHierarchy];
 
-    if(self.keyStateMachineOperation && ![self.keyStateMachineOperation isFinished]) {
-        if(state == nil) {
-            // we started this operation to move the state machine. Since you aren't asking for a state transition, and there's an active operation, no need to do anything
-            ckksnotice("ckkskey", self, "Not advancing state machine: waiting for %@", self.keyStateMachineOperation);
-            return;
-        }
+        CKKSResultOperation* fetchOp = [self.zoneChangeFetcher requestSuccessfulFetchForManyReasons:fetchReasons];
+        CKKSResultOperation* flagOp = [CKKSResultOperation named:@"post-fetch"
+                                                       withBlock:^{
+            STRONGIFY(self);
+            [self.stateMachine handleFlag:CKKSFlagFetchComplete];
+        }];
+        [flagOp addDependency:fetchOp];
+        [self scheduleOperation:flagOp];
+
+        return [OctagonStateTransitionOperation named:@"waiting-for-fetch" entering:SecCKKSZoneKeyStateFetch];
     }
 
-    if(state) {
-        self.keyStateMachineOperation = nil;
+    if([currentState isEqualToString:SecCKKSZoneKeyStateFetch]) {
+        if([flags _onqueueContains:CKKSFlagFetchComplete]) {
+            [flags _onqueueRemoveFlag:CKKSFlagFetchComplete];
+            return [OctagonStateTransitionOperation named:@"fetch-complete" entering:SecCKKSZoneKeyStateFetchComplete];
+        }
+
+        // The flags CKKSFlagCloudKitZoneMissing and CKKSFlagChangeTokenOutdated are both handled at the top of this function
+        // So, we don't need to handle them here.
 
-        ckksnotice("ckkskey", self, "Advancing key hierarchy state machine from %@ to %@", self.keyHierarchyState, state);
-        self.keyHierarchyState = state;
+        return nil;
     }
 
-    // Many of our decisions below will be based on what keys exist. Help them out.
-    CKKSCurrentKeySet* keyset = [[CKKSCurrentKeySet alloc] initForZone:self.zoneID];
-    NSError* localerror = nil;
-    NSArray<CKKSKey*>* localKeys = [CKKSKey localKeys:self.zoneID error:&localerror];
-    NSArray<CKKSKey*>* remoteKeys = [CKKSKey remoteKeys:self.zoneID error: &localerror];
+    if([currentState isEqualToString:SecCKKSZoneKeyStateNeedFullRefetch]) {
+         ckksnotice("ckkskey", self, "Starting a key hierarchy full refetch");
+
+         //TODO use states here instead of flags
+         self.keyStateMachineRefetched = true;
+         self.keyStateFullRefetchRequested = false;
 
-    // We also are checking for OutgoingQueueEntries in the reencrypt state; this is a sign that our key hierarchy is out of date.
-    NSArray<CKKSOutgoingQueueEntry*>* outdatedOQEs = [CKKSOutgoingQueueEntry allInState: SecCKKSStateReencrypt zoneID:self.zoneID error: &localerror];
+         return [OctagonStateTransitionOperation named:@"fetch-complete" entering:SecCKKSZoneKeyStateResettingLocalData];
+    }
 
-    SecADSetValueForScalarKey((__bridge CFStringRef) SecCKKSAggdViewKeyCount, [localKeys count]);
+    if([currentState isEqualToString:SecCKKSZoneKeyStateFetchComplete]) {
+        [self.launch addEvent:@"fetch-complete"];
+        [self.currentFetchReasons removeAllObjects];
 
-    if(localerror) {
-        ckkserror("ckkskey", self, "couldn't fetch keys and OQEs from local database, entering error state: %@", localerror);
-        self.keyHierarchyState = SecCKKSZoneKeyStateError;
-        self.keyHierarchyError = localerror;
-        return;
+        return [OctagonStateTransitionOperation named:@"post-fetch-process" entering:SecCKKSZoneKeyStateProcess];
     }
 
-#if !defined(NDEBUG)
-    NSArray<CKKSKey*>* allKeys = [CKKSKey allKeys:self.zoneID error:&localerror];
-    ckksdebug("ckkskey", self, "All keys: %@", allKeys);
-#endif
+    if([currentState isEqualToString:SecCKKSZoneKeyStateWaitForTLKCreation]) {
+        if([flags _onqueueContains:CKKSFlagKeyStateProcessRequested]) {
+            [flags _onqueueRemoveFlag:CKKSFlagKeyStateProcessRequested];
+            ckksnotice("ckkskey", self, "We believe we need to create TLKs but we also received a key nudge; moving to key state Process.");
+            return [OctagonStateTransitionOperation named:@"wait-for-tlk-creation-process" entering:SecCKKSZoneKeyStateProcess];
 
-    NSError* hierarchyError = nil;
+        } else if([flags _onqueueContains:CKKSFlagFetchRequested]) {
+            [flags _onqueueRemoveFlag:CKKSFlagFetchRequested];
+            return [OctagonStateTransitionOperation named:@"fetch-requested" entering:SecCKKSZoneKeyStateBeginFetch];
 
-    if([self.keyHierarchyState isEqualToString: SecCKKSZoneKeyStateInitializing]) {
-        if(state != nil) {
-            // Wait for CKKSZone to finish initialization.
-            ckkserror("ckkskey", self, "Asked to advance state machine (to %@) while CK zone still initializing.", state);
-        }
-        return;
+        } else if([flags _onqueueContains:CKKSFlagTLKCreationRequested]) {
+            [flags _onqueueRemoveFlag:CKKSFlagTLKCreationRequested];
 
-    } else if([self.keyHierarchyState isEqualToString: SecCKKSZoneKeyStateReady]) {
-        if(self.keyStateProcessRequested || [remoteKeys count] > 0) {
-            // We've either received some remote keys from the last fetch, or someone has requested a reprocess.
-            ckksnotice("ckkskey", self, "Kicking off a key reprocess based on request:%d and remote key count %lu", self.keyStateProcessRequested, (unsigned long)[remoteKeys count]);
-            [self _onqueueKeyHierarchyProcess];
+            // It's very likely that we're already untrusted at this point. But, sometimes we will be trusted right now, and can lose trust while waiting for the upload.
+            // This probably should be handled by a state increase.
+            [flags _onqueueRemoveFlag:CKKSFlagEndTrustedOperation];
 
-        } else if(self.keyStateFullRefetchRequested) {
-            // In ready, but someone has requested a full fetch. Kick it off.
-            ckksnotice("ckkskey", self, "Kicking off a key refetch based on request:%d", self.keyStateFetchRequested);
-            [self _onqueueKeyHierarchyRefetch];
+            ckksnotice("ckkskey", self, "TLK creation requested; kicking off operation");
+            return [[CKKSNewTLKOperation alloc] initWithDependencies:self.operationDependencies
+                                                                ckks:self];
+        } else if(self.lastNewTLKOperation.keyset) {
+            // This means that we _have_ created new TLKs, and should wait for them to be uploaded. This is ugly and should probably be done with more states.
+            return [OctagonStateTransitionOperation named:@"" entering:SecCKKSZoneKeyStateWaitForTLKUpload];
 
-        } else if(self.keyStateFetchRequested) {
-            // In ready, but someone has requested a fetch. Kick it off.
-            ckksnotice("ckkskey", self, "Kicking off a key refetch based on request:%d", self.keyStateFetchRequested);
-            [self _onqueueKeyHierarchyFetch];
+        } else {
+            ckksnotice("ckkskey", self, "We believe we need to create TLKs; waiting for Octagon (via %@)", self.suggestTLKUpload);
+            [self.suggestTLKUpload trigger];
         }
-        // TODO: kick off a key roll if one has been requested
+    }
 
-        if(!self.keyStateMachineOperation) {
-            // We think we're ready. Double check.
-            bool ready = [self _onqueueEnsureKeyHierarchyHealth:&hierarchyError];
-            if(!ready || hierarchyError) {
-                // Things is bad. Kick off a heal to fix things up.
-                ckksnotice("ckkskey", self, "Thought we were ready, but the key hierarchy is unhealthy: %@", hierarchyError);
-                self.keyHierarchyState = SecCKKSZoneKeyStateUnhealthy;
+    if([currentState isEqualToString:SecCKKSZoneKeyStateWaitForTLKUpload]) {
+        ckksnotice("ckkskey", self, "We believe we have TLKs that need uploading");
 
-            } else {
-                // In ready, nothing to do. Notify waiters and quit.
-                self.keyHierarchyOperationGroup = nil;
-                if(self.keyStateReadyDependency) {
-                    [self scheduleOperation: self.keyStateReadyDependency];
-                    self.keyStateReadyDependency = nil;
-                }
+        if([flags _onqueueContains:CKKSFlagFetchRequested]) {
+            ckksnotice("ckkskey", self, "Received a nudge to refetch CKKS");
+            return [OctagonStateTransitionOperation named:@"tlk-upload-refetch" entering:SecCKKSZoneKeyStateBeginFetch];
+        }
 
-                // If there are any OQEs waiting to be encrypted, launch an op to fix them
-                if([outdatedOQEs count] > 0u) {
-                    ckksnotice("ckksreencrypt", self, "Reencrypting outgoing items as the key hierarchy is ready");
-                    CKKSReencryptOutgoingItemsOperation* op = [[CKKSReencryptOutgoingItemsOperation alloc] initWithCKKSKeychainView:self ckoperationGroup:self.keyHierarchyOperationGroup];
-                    [self scheduleOperation:op];
-                }
+        if([flags _onqueueContains:CKKSFlagKeyStateTLKsUploaded]) {
+            [flags _onqueueRemoveFlag:CKKSFlagKeyStateTLKsUploaded];
 
-                return;
-            }
+            return [OctagonStateTransitionOperation named:@"wait-for-tlk-upload-process" entering:SecCKKSZoneKeyStateProcess];
         }
 
-    } else if([self.keyHierarchyState isEqualToString: SecCKKSZoneKeyStateInitialized]) {
-        // We're initialized and CloudKit is ready. See what needs done...
+        if([flags _onqueueContains:CKKSFlagEndTrustedOperation]) {
+            [flags _onqueueRemoveFlag:CKKSFlagEndTrustedOperation];
 
-        // Check if we have an existing key hierarchy
-        CKKSKey* tlk    = [CKKSKey currentKeyForClass:SecCKKSKeyClassTLK zoneID:self.zoneID error:&error];
-        CKKSKey* classA = [CKKSKey currentKeyForClass:SecCKKSKeyClassA   zoneID:self.zoneID error:&error];
-        CKKSKey* classC = [CKKSKey currentKeyForClass:SecCKKSKeyClassC   zoneID:self.zoneID error:&error];
+            return [OctagonStateTransitionOperation named:@"trust-loss" entering:SecCKKSZoneKeyStateLoseTrust];
+        }
 
-        if(error && !([error.domain isEqual: @"securityd"] && error.code == errSecItemNotFound)) {
-            ckkserror("ckkskey", self, "Error examining existing key hierarchy: %@", error);
+        if([flags _onqueueContains:CKKSFlagKeyStateProcessRequested]) {
+            return [OctagonStateTransitionOperation named:@"wait-for-tlk-fetch-process" entering:SecCKKSZoneKeyStateProcess];
         }
 
-        if(tlk && classA && classC && !error) {
-            // This is likely a restart of securityd, and we think we're ready. Double check.
-            bool ready = [self _onqueueEnsureKeyHierarchyHealth:&hierarchyError];
-            if(ready && !hierarchyError) {
-                ckksnotice("ckkskey", self, "Already have existing key hierarchy for %@; using it.", self.zoneID.zoneName);
-            } else if(hierarchyError && [self.lockStateTracker isLockedError:hierarchyError]) {
-                ckksnotice("ckkskey", self, "Initial scan shows key hierarchy is unavailable since keychain is locked: %@", hierarchyError);
-                self.keyHierarchyState = SecCKKSZoneKeyStateWaitForUnlock;
-            } else {
-                ckksnotice("ckkskey", self, "Initial scan shows key hierarchy is unhealthy: %@", hierarchyError);
-                self.keyHierarchyState = SecCKKSZoneKeyStateUnhealthy;
-            }
+        // This is quite the hack, but it'll do for now.
+        [self.operationDependencies provideKeySet:self.lastNewTLKOperation.keyset];
 
-        } else {
-            // We have no local key hierarchy. One might exist in CloudKit, or it might not.
-            ckksnotice("ckkskey", self, "No existing key hierarchy for %@. Check if there's one in CloudKit...", self.zoneID.zoneName);
+        ckksnotice("ckkskey", self, "Notifying Octagon again, just in case");
+        [self.suggestTLKUpload trigger];
+    }
 
-            [self _onqueueKeyHierarchyFetch];
-        }
+    if([currentState isEqualToString:SecCKKSZoneKeyStateTLKMissing]) {
+        return [self tlkMissingOperation:SecCKKSZoneKeyStateWaitForTLK];
+    }
+
+    if([currentState isEqualToString:SecCKKSZoneKeyStateWaitForTLK]) {
+        // We're in a hold state: waiting for the TLK bytes to arrive.
 
-    } else if([self.keyHierarchyState isEqualToString: SecCKKSZoneKeyStateFetchComplete]) {
-        // We're initializing this zone, and just completed a fetch of everything. Are there any remote keys?
-        if(remoteKeys.count > 0u) {
-            // Process the keys we received.
-            self.keyStateMachineOperation = [[CKKSProcessReceivedKeysOperation alloc] initWithCKKSKeychainView: self];
-        } else if( (keyset.currentTLKPointer || keyset.currentClassAPointer || keyset.currentClassCPointer) &&
-                  !(keyset.tlk && keyset.classA && keyset.classC)) {
-            // Huh. We appear to have current key pointers, but the keys themselves don't exist. That's weird.
-            // Transfer to the "unhealthy" state to request a fix
-            ckksnotice("ckkskey", self, "We appear to have current key pointers but no keys to match them. Moving to 'unhealthy'");
-            self.keyHierarchyState = SecCKKSZoneKeyStateUnhealthy;
+        if([flags _onqueueContains:CKKSFlagKeyStateProcessRequested]) {
+            [flags _onqueueRemoveFlag:CKKSFlagKeyStateProcessRequested];
+            // Someone has requsted a reprocess! Go to the correct state.
+            ckksnotice("ckkskey", self, "Received a nudge that our TLK might be here! Reprocessing.");
+            return [OctagonStateTransitionOperation named:@"wait-for-tlk-process" entering:SecCKKSZoneKeyStateProcess];
 
-        } else if([remoteKeys count] == 0) {
-            // No keys, no pointers? make some new ones!
-            self.keyStateMachineOperation = [[CKKSNewTLKOperation alloc] initWithCKKSKeychainView: self ckoperationGroup:self.keyHierarchyOperationGroup];
+        } else if([flags _onqueueContains:CKKSFlagTrustedPeersSetChanged]) {
+            [flags _onqueueRemoveFlag:CKKSFlagTrustedPeersSetChanged];
+
+            // Hmm, maybe this trust set change will cause us to recover this TLK (due to a previously-untrusted share becoming trusted). Worth a shot!
+            ckksnotice("ckkskey", self, "Received a nudge that the trusted peers set might have changed! Reprocessing.");
+            return [OctagonStateTransitionOperation named:@"wait-for-tlk-peers" entering:SecCKKSZoneKeyStateProcess];
         }
 
-    } else if([self.keyHierarchyState isEqualToString: SecCKKSZoneKeyStateWaitForTLK]) {
-        // We're in a hold state: waiting for the TLK bytes to arrive.
+        return nil;
+    }
 
-        if(self.keyStateProcessRequested) {
-            // Someone has requsted a reprocess! Run a ProcessReceivedKeysOperation.
-            ckksnotice("ckkskey", self, "Received a nudge that our TLK might be here! Starting operation to check.");
-            [self _onqueueKeyHierarchyProcess];
+    if([currentState isEqualToString:SecCKKSZoneKeyStateWaitForUnlock]) {
+        ckksnotice("ckkskey", self, "Requested to enter waitforunlock");
+
+        if([flags _onqueueContains:CKKSFlagDeviceUnlocked ]) {
+            [flags _onqueueRemoveFlag:CKKSFlagDeviceUnlocked];
+            return [OctagonStateTransitionOperation named:@"key-state-after-unlock" entering:SecCKKSZoneKeyStateInitialized];
         }
 
-    } else if([self.keyHierarchyState isEqualToString: SecCKKSZoneKeyStateWaitForUnlock]) {
-        // We're in a hold state: waiting for the keybag to unlock so we can process the keys again.
+        OctagonPendingFlag* unlocked = [[OctagonPendingFlag alloc] initWithFlag:CKKSFlagDeviceUnlocked
+                                                                     conditions:OctagonPendingConditionsDeviceUnlocked];
+        [pendingFlagHandler _onqueueHandlePendingFlag:unlocked];
 
-        [self _onqueueKeyHierarchyProcess];
-        [self.keyStateMachineOperation addNullableDependency: self.lockStateTracker.unlockDependency];
+        return nil;
+    }
 
-    } else if([self.keyHierarchyState isEqualToString: SecCKKSZoneKeyStateBadCurrentPointers]) {
+    if([currentState isEqualToString:SecCKKSZoneKeyStateBadCurrentPointers]) {
         // The current key pointers are broken, but we're not sure why.
         ckksnotice("ckkskey", self, "Our current key pointers are reported broken. Attempting a fix!");
-        self.keyStateMachineOperation = [[CKKSHealKeyHierarchyOperation alloc] initWithCKKSKeychainView: self ckoperationGroup:self.keyHierarchyOperationGroup];
+        return [[CKKSHealKeyHierarchyOperation alloc] initWithDependencies:self.operationDependencies
+                                                                      ckks:self
+                                                                 intending:SecCKKSZoneKeyStateBecomeReady
+                                                                errorState:SecCKKSZoneKeyStateError];
+    }
 
-    } else if([self.keyHierarchyState isEqualToString: SecCKKSZoneKeyStateNewTLKsFailed]) {
+    if([currentState isEqualToString:SecCKKSZoneKeyStateNewTLKsFailed]) {
         ckksnotice("ckkskey", self, "Creating new TLKs didn't work. Attempting to refetch!");
-        [self _onqueueKeyHierarchyFetch];
-
-    } else if([self.keyHierarchyState isEqualToString: SecCKKSZoneKeyStateNeedFullRefetch]) {
-        ckksnotice("ckkskey", self, "Informed of request for full refetch");
-        [self _onqueueKeyHierarchyRefetch];
-
-    } else {
-        ckkserror("ckks", self, "asked to advance state machine to unknown state: %@", self.keyHierarchyState);
-        return;
+        return [OctagonStateTransitionOperation named:@"new-tlks-failed" entering:SecCKKSZoneKeyStateBeginFetch];
     }
 
-    if(self.keyStateMachineOperation) {
+    if([currentState isEqualToString:SecCKKSZoneKeyStateHealTLKSharesFailed]) {
+        ckksnotice("ckkskey", self, "Creating new TLK shares didn't work. Attempting to refetch!");
+        return [OctagonStateTransitionOperation named:@"heal-tlks-failed" entering:SecCKKSZoneKeyStateBeginFetch];
+    }
 
-        if(self.keyStateReadyDependency == nil || [self.keyStateReadyDependency isFinished]) {
-            ckksnotice("ckkskey", self, "reloading keyStateReadyDependency due to operation %@", self.keyStateMachineOperation);
+    if([currentState isEqualToString:SecCKKSZoneKeyStateUnhealthy]) {
+        if(self.trustStatus != CKKSAccountStatusAvailable) {
+            ckksnotice("ckkskey", self, "Looks like the key hierarchy is unhealthy, but we're untrusted.");
+            return [OctagonStateTransitionOperation named:@"unhealthy-lacking-trust" entering:SecCKKSZoneKeyStateLoseTrust];
 
-            __weak __typeof(self) weakSelf = self;
-            self.keyHierarchyOperationGroup = [CKOperationGroup CKKSGroupWithName:@"key-state-broken"];
-            self.keyStateReadyDependency = [CKKSResultOperation operationWithBlock:^{
-                ckksnotice("ckkskey", weakSelf, "Key state has become ready again.");
-            }];
-            self.keyStateReadyDependency.name = [NSString stringWithFormat: @"%@-key-state-ready", self.zoneName];
+        } else {
+            ckksnotice("ckkskey", self, "Looks like the key hierarchy is unhealthy. Launching fix.");
+            return [[CKKSHealKeyHierarchyOperation alloc] initWithDependencies:self.operationDependencies
+                                                                          ckks:self
+                                                                     intending:SecCKKSZoneKeyStateBecomeReady
+                                                                    errorState:SecCKKSZoneKeyStateError];
         }
+    }
 
-        [self scheduleOperation: self.keyStateMachineOperation];
-    } else if([self.keyHierarchyState isEqualToString:SecCKKSZoneKeyStateWaitForTLK]) {
-        ckksnotice("ckkskey", self, "Entering %@", self.keyHierarchyState);
+    if([currentState isEqualToString:SecCKKSZoneKeyStateHealTLKShares]) {
+        ckksnotice("ckksshare", self, "Key hierarchy is okay, but not shared appropriately. Launching fix.");
+        return [[CKKSHealTLKSharesOperation alloc] initWithOperationDependencies:self.operationDependencies
+                                                                            ckks:self];
+    }
 
-    } else if([self.keyHierarchyState isEqualToString:SecCKKSZoneKeyStateUnhealthy]) {
-        ckksnotice("ckkskey", self, "Looks like the key hierarchy is unhealthy. Launching fix.");
-        self.keyStateMachineOperation = [[CKKSHealKeyHierarchyOperation alloc] initWithCKKSKeychainView:self ckoperationGroup:self.keyHierarchyOperationGroup];
-        [self scheduleOperation: self.keyStateMachineOperation];
+    if([currentState isEqualToString:SecCKKSZoneKeyStateProcess]) {
+        [flags _onqueueRemoveFlag:CKKSFlagKeyStateProcessRequested];
 
-    } else {
-        // Nothing to do and not in a waiting state? Awesome; we must be in the ready state.
-        if(![self.keyHierarchyState isEqual: SecCKKSZoneKeyStateReady]) {
-            ckksnotice("ckkskey", self, "No action to take in state %@; we must be ready.", self.keyHierarchyState);
-            self.keyHierarchyState = SecCKKSZoneKeyStateReady;
-
-            self.keyHierarchyOperationGroup = nil;
-            if(self.keyStateReadyDependency) {
-                [self scheduleOperation: self.keyStateReadyDependency];
-                self.keyStateReadyDependency = nil;
-            }
-        }
+        ckksnotice("ckksshare", self, "Launching key state process");
+        return [[CKKSProcessReceivedKeysOperation alloc] initWithDependencies:self.operationDependencies
+                                                                intendedState:SecCKKSZoneKeyStateBecomeReady
+                                                                   errorState:SecCKKSZoneKeyStateError];
     }
-}
 
-- (bool)_onqueueEnsureKeyHierarchyHealth:(NSError* __autoreleasing *)error {
-    dispatch_assert_queue(self.queue);
+    return nil;
+}
 
-    NSError* localerror = nil;
+- (OctagonStateTransitionOperation*)tlkMissingOperation:(CKKSZoneKeyState*)newState
+{
+    WEAKIFY(self);
+    return [OctagonStateTransitionOperation named:@"tlk-missing"
+                                        intending:newState
+                                       errorState:SecCKKSZoneKeyStateError
+                              withBlockTakingSelf:^(OctagonStateTransitionOperation * _Nonnull op) {
+        STRONGIFY(self);
 
-    // Check if we have an existing key hierarchy
-    CKKSKey* tlk    = [CKKSKey currentKeyForClass:SecCKKSKeyClassTLK zoneID:self.zoneID error:&localerror];
-    CKKSKey* classA = [CKKSKey currentKeyForClass:SecCKKSKeyClassA   zoneID:self.zoneID error:&localerror];
-    CKKSKey* classC = [CKKSKey currentKeyForClass:SecCKKSKeyClassC   zoneID:self.zoneID error:&localerror];
+        NSArray<CKKSPeerProviderState*>* trustStates = self.operationDependencies.currentTrustStates;
 
-    if(localerror || !tlk || !classA || !classC) {
-        ckkserror("ckkskey", self, "Error examining existing key hierarchy: %@", localerror);
-        ckkserror("ckkskey", self, "Keys are: %@ %@ %@", tlk, classA, classC);
-        if(error) {
-            *error = localerror;
-        }
-        return false;
-    }
+        [self.operationDependencies.databaseProvider dispatchSyncWithReadOnlySQLTransaction:^{
+            CKKSCurrentKeySet* keyset = [CKKSCurrentKeySet loadForZone:self.zoneID];
 
-    // keychain being locked is not a fatal error here
-    [tlk loadKeyMaterialFromKeychain:&localerror];
-    if(localerror && !([localerror.domain isEqual: @"securityd"] && localerror.code == errSecInteractionNotAllowed)) {
-        ckksinfo("ckkskey", self, "Error loading TLK(%@): %@", tlk, localerror);
-        if(error) {
-            *error = localerror;
-        }
-        return false;
-    } else if(localerror) {
-        ckksinfo("ckkskey", self, "Error loading TLK(%@), maybe locked: %@", tlk, localerror);
-    }
-    localerror = nil;
+            if(keyset.error) {
+                ckkserror("ckkskey", self, "Unable to load keyset: %@", keyset.error);
+                op.nextState = newState;
 
-    // keychain being locked is not a fatal error here
-    [classA loadKeyMaterialFromKeychain:&localerror];
-    if(localerror && !([localerror.domain isEqual: @"securityd"] && localerror.code == errSecInteractionNotAllowed)) {
-        ckksinfo("ckkskey", self, "Error loading classA key(%@): %@", classA, localerror);
-        if(error) {
-            *error = localerror;
-        }
-        return false;
-    } else if(localerror) {
-        ckksinfo("ckkskey", self, "Error loading classA key(%@), maybe locked: %@", classA, localerror);
-    }
-    localerror = nil;
+                [self.operationDependencies provideKeySet:keyset];
+                return;
+            }
 
-    // keychain being locked is a fatal error here, since this is class C
-    [classA loadKeyMaterialFromKeychain:&localerror];
-    if(localerror) {
-        ckksinfo("ckkskey", self, "Error loading classC(%@): %@", classC, localerror);
-        if(error) {
-            *error = localerror;
-        }
-        return false;
-    }
+            if(!keyset.currentTLKPointer.currentKeyUUID) {
+                // In this case, there's no current TLK at all. Go into "wait for tlkcreation";
+                op.nextState = SecCKKSZoneKeyStateWaitForTLKCreation;
+                [self.operationDependencies provideKeySet:keyset];
+                return;
+            }
 
-    self.activeTLK = [tlk uuid];
+            if(self.trustStatus != CKKSAccountStatusAvailable) {
+                ckksnotice("ckkskey", self, "TLK is missing, but no trust is present.");
+                op.nextState = SecCKKSZoneKeyStateLoseTrust;
 
-    // Got to the bottom? Cool! All keys are present and accounted for.
-    return true;
-}
+                [self.operationDependencies provideKeySet:keyset];
+                return;
+            }
 
-- (void)_onqueueKeyHierarchyFetch {
-    dispatch_assert_queue(self.queue);
+            bool otherDevicesPresent = [self _onqueueOtherDevicesReportHavingTLKs:keyset
+                                                                      trustStates:trustStates];
+            if(otherDevicesPresent) {
+                // We expect this keyset to continue to exist. Send it to our listeners.
+                [self.operationDependencies provideKeySet:keyset];
 
-    __weak __typeof(self) weakSelf = self;
-    self.keyStateMachineOperation = [NSBlockOperation blockOperationWithBlock: ^{
-        __strong __typeof(weakSelf) strongSelf = weakSelf;
-        if(!strongSelf) {
-            ckkserror("ckks", strongSelf, "received callback for released object");
+                op.nextState = newState;
+            } else {
+                ckksnotice("ckkskey", self, "No other devices claim to have the TLK. Resetting zone...");
+                op.nextState = SecCKKSZoneKeyStateResettingZone;
+            }
             return;
-        }
-
-        [strongSelf dispatchSync: ^bool{
-            [strongSelf _onqueueAdvanceKeyStateMachineToState: SecCKKSZoneKeyStateFetchComplete withError: nil];
-            return true;
         }];
     }];
-    self.keyStateMachineOperation.name = @"waiting-for-fetch";
+}
 
-    NSOperation* fetchOp = [self.zoneChangeFetcher requestSuccessfulFetch: CKKSFetchBecauseKeyHierarchy];
-    [self.keyStateMachineOperation addDependency: fetchOp];
+- (bool)_onqueueOtherDevicesReportHavingTLKs:(CKKSCurrentKeySet*)keyset
+                                 trustStates:(NSArray<CKKSPeerProviderState*>*)trustStates
+{
+    //Has there been any activity indicating that other trusted devices have keys in the past 45 days, or untrusted devices in the past 4?
+    // (We chose 4 as devices attempt to upload their device state every 3 days. If a device is unceremoniously kicked out of circle, we normally won't immediately reset.)
+    NSDate* now = [NSDate date];
+    NSDateComponents* trustedOffset = [[NSDateComponents alloc] init];
+    [trustedOffset setDay:-45];
+    NSDate* trustedDeadline = [[NSCalendar currentCalendar] dateByAddingComponents:trustedOffset toDate:now options:0];
 
-    self.keyStateFetchRequested = false;
-}
+    NSDateComponents* untrustedOffset = [[NSDateComponents alloc] init];
+    [untrustedOffset setDay:-4];
+    NSDate* untrustedDeadline = [[NSCalendar currentCalendar] dateByAddingComponents:untrustedOffset toDate:now options:0];
 
-- (void)_onqueueKeyHierarchyRefetch {
-    dispatch_assert_queue(self.queue);
 
-    __weak __typeof(self) weakSelf = self;
-    self.keyStateMachineOperation = [NSBlockOperation blockOperationWithBlock: ^{
-        __strong __typeof(weakSelf) strongSelf = weakSelf;
-        if(!strongSelf) {
-            ckkserror("ckks", strongSelf, "received callback for released object");
-            return;
+    NSMutableSet<NSString*>* trustedPeerIDs = [NSMutableSet set];
+    for(CKKSPeerProviderState* trustState in trustStates) {
+        for(id<CKKSPeer> peer in trustState.currentTrustedPeers) {
+            [trustedPeerIDs addObject:peer.peerID];
         }
+    }
 
-        [strongSelf dispatchSync: ^bool{
-            [strongSelf _onqueueAdvanceKeyStateMachineToState: SecCKKSZoneKeyStateFetchComplete withError: nil];
-            return true;
-        }];
-    }];
-    self.keyStateMachineOperation.name = @"waiting-for-refetch";
+    NSError* localerror = nil;
 
-    NSOperation* fetchOp = [self.zoneChangeFetcher requestSuccessfulResyncFetch: CKKSFetchBecauseKeyHierarchy];
-    [self.keyStateMachineOperation addDependency: fetchOp];
+    NSArray<CKKSDeviceStateEntry*>* allDeviceStates = [CKKSDeviceStateEntry allInZone:keyset.currentTLKPointer.zoneID error:&localerror];
+    if(localerror) {
+        ckkserror("ckkskey", self, "Error fetching device states: %@", localerror);
+        localerror = nil;
+        return true;
+    }
+    for(CKKSDeviceStateEntry* device in allDeviceStates) {
+        // The peerIDs in CDSEs aren't written with the peer prefix. Make sure we match both.
+        NSString* sosPeerID = device.circlePeerID ? [CKKSSOSPeerPrefix stringByAppendingString:device.circlePeerID] : nil;
+
+        if([trustedPeerIDs containsObject:device.circlePeerID] ||
+           [trustedPeerIDs containsObject:sosPeerID] ||
+           [trustedPeerIDs containsObject:device.octagonPeerID]) {
+            // Is this a recent DSE? If it's older than the deadline, skip it
+            if([device.storedCKRecord.modificationDate compare:trustedDeadline] == NSOrderedAscending) {
+                ckksnotice("ckkskey", self, "Trusted device state (%@) is too old; ignoring", device);
+                continue;
+            }
+        } else {
+            // Device is untrusted. How does it fare with the untrustedDeadline?
+            if([device.storedCKRecord.modificationDate compare:untrustedDeadline] == NSOrderedAscending) {
+                ckksnotice("ckkskey", self, "Device (%@) is not trusted and from too long ago; ignoring device state (%@)", device.circlePeerID, device);
+                continue;
+            } else {
+                ckksnotice("ckkskey", self, "Device (%@) is not trusted, but very recent. Including in heuristic: %@", device.circlePeerID, device);
+            }
+        }
 
-    self.keyStateMachineRefetched = true;
-    self.keyStateFullRefetchRequested = false;
-    self.keyStateFetchRequested = false;
-}
+        if([device.keyState isEqualToString:SecCKKSZoneKeyStateReady] ||
+           [device.keyState isEqualToString:SecCKKSZoneKeyStateReadyPendingUnlock]) {
+            ckksnotice("ckkskey", self, "Other device (%@) has keys; it should send them to us", device);
+            return true;
+        }
+    }
 
+    NSArray<CKKSTLKShareRecord*>* tlkShares = [CKKSTLKShareRecord allForUUID:keyset.currentTLKPointer.currentKeyUUID
+                                                                      zoneID:keyset.currentTLKPointer.zoneID
+                                                                       error:&localerror];
+    if(localerror) {
+        ckkserror("ckkskey", self, "Error fetching device states: %@", localerror);
+        localerror = nil;
+        return false;
+    }
 
-- (void)_onqueueKeyHierarchyProcess {
-    dispatch_assert_queue(self.queue);
+    for(CKKSTLKShareRecord* tlkShare in tlkShares) {
+        if([trustedPeerIDs containsObject:tlkShare.senderPeerID] &&
+           [tlkShare.storedCKRecord.modificationDate compare:trustedDeadline] == NSOrderedDescending) {
+            ckksnotice("ckkskey", self, "Trusted TLK Share (%@) created recently; other devices have keys and should send them to us", tlkShare);
+            return true;
+        }
+    }
 
-    self.keyStateMachineOperation = [[CKKSProcessReceivedKeysOperation alloc] initWithCKKSKeychainView: self];
+    // Okay, how about the untrusted deadline?
+    for(CKKSTLKShareRecord* tlkShare in tlkShares) {
+        if([tlkShare.storedCKRecord.modificationDate compare:untrustedDeadline] == NSOrderedDescending) {
+            ckksnotice("ckkskey", self, "Untrusted TLK Share (%@) created very recently; other devices might have keys and should rejoin the circle (and send them to us)", tlkShare);
+            return true;
+        }
+    }
 
-    // Since we're starting a reprocess, this is answering all previous requests.
-    self.keyStateProcessRequested = false;
+    return false;
 }
 
-- (void) handleKeychainEventDbConnection: (SecDbConnectionRef) dbconn
-                                   added: (SecDbItemRef) added
-                                 deleted: (SecDbItemRef) deleted
-                             rateLimiter: (CKKSRateLimiter*) rateLimiter
-                            syncCallback: (SecBoolNSErrorCallback) syncCallback {
+- (void)handleKeychainEventDbConnection:(SecDbConnectionRef) dbconn
+                                 source:(SecDbTransactionSource)txionSource
+                                  added:(SecDbItemRef) added
+                                deleted:(SecDbItemRef) deleted
+                            rateLimiter:(CKKSRateLimiter*) rateLimiter
+{
     if(!SecCKKSIsEnabled()) {
-        ckksinfo("ckks", self, "Skipping handleKeychainEventDbConnection due to disabled CKKS");
+        ckksnotice("ckks", self, "Skipping handleKeychainEventDbConnection due to disabled CKKS");
         return;
     }
 
     __block NSError* error = nil;
 
-    if(self.accountStatus != CKKSAccountStatusAvailable && syncCallback) {
-        // We're not logged into CloudKit, and therefore don't expect this item to be synced anytime particularly soon.
-        CKKSAccountStatus accountStatus = self.accountStatus;
-        dispatch_async(self.queue, ^{
-            syncCallback(false, [NSError errorWithDomain:@"securityd"
-                                                    code:errSecNotLoggedIn
-                                                userInfo:@{NSLocalizedDescriptionKey:
-                                                               [NSString stringWithFormat: @"No iCloud account available(%d); item is not expected to sync", (int)accountStatus]}]);
-        });
-
-        syncCallback = nil;
-    }
-
     // Tombstones come in as item modifications or item adds. Handle modifications here.
     bool addedTombstone   = added   && SecDbItemIsTombstone(added);
     bool deletedTombstone = deleted && SecDbItemIsTombstone(deleted);
     bool addedSync   = added   && SecDbItemIsSyncable(added);
     bool deletedSync = deleted && SecDbItemIsSyncable(deleted);
 
+    bool isTombstoneModification = addedTombstone && deletedTombstone;
     bool isAdd    = ( added && !deleted) || (added && deleted && !addedTombstone &&  deletedTombstone) || (added && deleted &&  addedSync && !deletedSync);
     bool isDelete = (!added &&  deleted) || (added && deleted &&  addedTombstone && !deletedTombstone) || (added && deleted && !addedSync &&  deletedSync);
     bool isModify = ( added &&  deleted) && (!isAdd) && (!isDelete);
     bool proceed = addedSync || deletedSync;
 
     if(!proceed) {
-        ckksinfo("ckks", self, "skipping sync of non-sync item");
+        ckksnotice("ckks", self, "skipping sync of non-sync item (%d, %d)", addedSync, deletedSync);
+        return;
+    }
+
+    if(isTombstoneModification) {
+        ckksnotice("ckks", self, "skipping syncing update of tombstone item (%d, %d)", addedTombstone, deletedTombstone);
+        return;
+    }
+
+    // It's possible to ask for an item to be deleted without adding a corresponding tombstone.
+    // This is arguably a bug, as it generates an out-of-sync state, but it is in the API contract.
+    // CKKS should ignore these, but log very upset messages.
+    if(isDelete && !addedTombstone) {
+        ckksnotice("ckks", self, "Client has asked for an item deletion to not sync. Keychain is now out of sync with account");
         return;
     }
 
     NSString* protection = (__bridge NSString*)SecDbItemGetCachedValueWithName(added ? added : deleted, kSecAttrAccessible);
     if(! ([protection isEqualToString: (__bridge NSString*)kSecAttrAccessibleWhenUnlocked] ||
           [protection isEqualToString: (__bridge NSString*)kSecAttrAccessibleAfterFirstUnlock] ||
-          [protection isEqualToString: (__bridge NSString*)kSecAttrAccessibleAlways])) {
-        ckksinfo("ckks", self, "skipping sync of device-bound item");
+          [protection isEqualToString: (__bridge NSString*)kSecAttrAccessibleAlwaysPrivate])) {
+        ckksnotice("ckks", self, "skipping sync of device-bound(%@) item", protection);
         return;
     }
 
+    if(txionSource == kSecDbSOSTransaction) {
+        NSString* addedUUID = (__bridge NSString*)SecDbItemGetValue(added, &v10itemuuid, NULL);
+        ckksnotice("ckks", self, "Received an incoming %@ from SOS (%@)",
+                   isAdd ? @"addition" : (isModify ? @"modification" : @"deletion"),
+                   addedUUID);
+    }
+
     // Our caller gave us a database connection. We must get on the local queue to ensure atomicity
     // Note that we're at the mercy of the surrounding db transaction, so don't try to rollback here
-    [self dispatchSyncWithConnection: dbconn block: ^bool {
-        if(![self.keyHierarchyState isEqualToString: SecCKKSZoneKeyStateReady]) {
-            ckksnotice("ckks", self, "Key state not ready for new items; skipping");
-            return true;
+    [self dispatchSyncWithConnection:dbconn
+                      readWriteTxion:YES
+                               block:^CKKSDatabaseTransactionResult {
+        // Schedule a "view changed" notification
+        [self.notifyViewChangedScheduler trigger];
+
+        if(self.accountStatus == CKKSAccountStatusNoAccount) {
+            // No account; CKKS shouldn't attempt anything.
+            [self.stateMachine _onqueueHandleFlag:CKKSFlagScanLocalItems];
+            ckksnotice("ckks", self, "Dropping sync item modification due to CK account state; will scan to find changes later");
+
+            // We're positively not logged into CloudKit, and therefore don't expect this item to be synced anytime particularly soon.
+            NSString* uuid = (__bridge NSString*)SecDbItemGetValue(added ? added : deleted, &v10itemuuid, NULL);
+
+            SecBoolNSErrorCallback syncCallback = [[CKKSViewManager manager] claimCallbackForUUID:uuid];
+            if(syncCallback) {
+                [CKKSViewManager callSyncCallbackWithErrorNoAccount: syncCallback];
+            }
+
+            return CKKSDatabaseTransactionCommit;
         }
 
         CKKSOutgoingQueueEntry* oqe = nil;
         if       (isAdd) {
-            oqe = [CKKSOutgoingQueueEntry withItem: added   action: SecCKKSActionAdd    ckks:self error: &error];
+            oqe = [CKKSOutgoingQueueEntry withItem: added   action: SecCKKSActionAdd    zoneID:self.zoneID error: &error];
         } else if(isDelete) {
-            oqe = [CKKSOutgoingQueueEntry withItem: deleted action: SecCKKSActionDelete ckks:self error: &error];
+            oqe = [CKKSOutgoingQueueEntry withItem: deleted action: SecCKKSActionDelete zoneID:self.zoneID error: &error];
         } else if(isModify) {
-            oqe = [CKKSOutgoingQueueEntry withItem: added   action: SecCKKSActionModify ckks:self error: &error];
+            oqe = [CKKSOutgoingQueueEntry withItem: added   action: SecCKKSActionModify zoneID:self.zoneID error: &error];
         } else {
             ckkserror("ckks", self, "processKeychainEventItemAdded given garbage: %@ %@", added, deleted);
-            return true;
+            return CKKSDatabaseTransactionCommit;
+        }
+
+        if(!self.itemSyncingEnabled) {
+            // Call any callback now; they're not likely to get the sync they wanted
+            SecBoolNSErrorCallback syncCallback = [[CKKSViewManager manager] claimCallbackForUUID:oqe.uuid];
+            if(syncCallback) {
+                syncCallback(false, [NSError errorWithDomain:CKKSErrorDomain
+                                                        code:CKKSErrorViewIsPaused
+                                                 description:@"View is paused; item is not expected to sync"]);
+            }
         }
 
-        CKOperationGroup* operationGroup = [CKOperationGroup CKKSGroupWithName:@"keychain-api-use"];
+        CKOperationGroup* operationGroup = txionSource == kSecDbSOSTransaction
+            ? [CKOperationGroup CKKSGroupWithName:@"sos-incoming-item"]
+            : [CKOperationGroup CKKSGroupWithName:@"keychain-api-use"];
 
         if(error) {
             ckkserror("ckks", self, "Couldn't create outgoing queue entry: %@", error);
-
-            // If the problem is 'no UUID', launch a scan operation to find and fix it
-            // We don't want to fix it up here, in the closing moments of a transaction
-            if([error.domain isEqualToString:@"securityd"] && error.code == CKKSNoUUIDOnItem) {
-                ckksnotice("ckks", self, "Launching scan operation");
-                CKKSScanLocalItemsOperation* scanOperation = [[CKKSScanLocalItemsOperation alloc] initWithCKKSKeychainView: self ckoperationGroup:operationGroup];
-                [self scheduleOperation: scanOperation];
-            }
+            [self.stateMachine _onqueueHandleFlag:CKKSFlagScanLocalItems];
 
             // If the problem is 'couldn't load key', tell the key hierarchy state machine to fix it
-            // Then, launch a scan operation to find this item and upload it
-            if([error.domain isEqualToString:@"securityd"] && error.code == errSecItemNotFound) {
-                [self _onqueueAdvanceKeyStateMachineToState: nil withError: nil];
-
-                ckksnotice("ckks", self, "Launching scan operation to refind %@", added);
-                CKKSScanLocalItemsOperation* scanOperation = [[CKKSScanLocalItemsOperation alloc] initWithCKKSKeychainView: self ckoperationGroup:operationGroup];
-                [scanOperation addNullableDependency:self.keyStateReadyDependency];
-                [self scheduleOperation: scanOperation];
+            if([error.domain isEqualToString:CKKSErrorDomain] && error.code == errSecItemNotFound) {
+                [self.stateMachine _onqueueHandleFlag:CKKSFlagKeyStateProcessRequested];
             }
 
-            return true;
+            return CKKSDatabaseTransactionCommit;
+        } else if(!oqe) {
+            ckkserror("ckks", self, "Decided that no operation needs to occur for %@", error);
+            return CKKSDatabaseTransactionCommit;
         }
 
         if(rateLimiter) {
         [oqe saveToDatabaseWithConnection: dbconn error: &error];
         if(error) {
             ckkserror("ckks", self, "Couldn't save outgoing queue entry to database: %@", error);
-            return true;
+            return CKKSDatabaseTransactionCommit;
+        } else {
+            ckksnotice("ckks", self, "Saved %@ to outgoing queue", oqe);
         }
 
         // This update supercedes all other local modifications to this item (_except_ those in-flight).
         // Delete all items in reencrypt or error.
-        CKKSOutgoingQueueEntry* reencryptOQE = [CKKSOutgoingQueueEntry tryFromDatabase:oqe.uuid state:SecCKKSStateReencrypt zoneID:self.zoneID error:&error];
+        NSArray<CKKSOutgoingQueueEntry*>* siblings = [CKKSOutgoingQueueEntry allWithUUID:oqe.uuid
+                                                                                  states:@[SecCKKSStateReencrypt, SecCKKSStateError]
+                                                                                  zoneID:self.zoneID
+                                                                                   error:&error];
         if(error) {
-            ckkserror("ckks", self, "Couldn't load reencrypt OQE sibling for %@: %@", oqe, error);
-        }
-        if(reencryptOQE) {
-            [reencryptOQE deleteFromDatabase:&error];
-            if(error) {
-                ckkserror("ckks", self, "Couldn't delete reencrypt OQE sibling(%@) for %@: %@", reencryptOQE, oqe, error);
-            }
-            error = nil;
+            ckkserror("ckks", self, "Couldn't load OQE siblings for %@: %@", oqe, error);
         }
 
-        CKKSOutgoingQueueEntry* errorOQE = [CKKSOutgoingQueueEntry tryFromDatabase:oqe.uuid state:SecCKKSStateError zoneID:self.zoneID error:&error];
-        if(error) {
-            ckkserror("ckks", self, "Couldn't load error OQE sibling for %@: %@", oqe, error);
-        }
-        if(errorOQE) {
-            [errorOQE deleteFromDatabase:&error];
-            if(error) {
-                ckkserror("ckks", self, "Couldn't delete error OQE sibling(%@) for %@: %@", reencryptOQE, oqe, error);
+        for(CKKSOutgoingQueueEntry* oqeSibling in siblings) {
+            NSError* deletionError = nil;
+            [oqeSibling deleteFromDatabase:&deletionError];
+            if(deletionError) {
+                ckkserror("ckks", self, "Couldn't delete OQE sibling(%@) for %@: %@", oqeSibling, oqe.uuid, deletionError);
             }
         }
 
-        if(syncCallback) {
-            self.pendingSyncCallbacks[oqe.uuid] = syncCallback;
+        // This update also supercedes any remote changes that are pending.
+        NSError* iqeError = nil;
+        CKKSIncomingQueueEntry* iqe = [CKKSIncomingQueueEntry tryFromDatabase:oqe.uuid zoneID:self.zoneID error:&iqeError];
+        if(iqeError) {
+            ckkserror("ckks", self, "Couldn't find IQE matching %@: %@", oqe.uuid, error);
+        } else if(iqe) {
+            [iqe deleteFromDatabase:&iqeError];
+            if(iqeError) {
+                ckkserror("ckks", self, "Couldn't delete IQE matching %@: %@", oqe.uuid, error);
+            } else {
+                ckksnotice("ckks", self, "Deleted IQE matching changed item %@", oqe.uuid);
+            }
         }
 
-        // Schedule a "view changed" notification
-        [self.notifyViewChangedScheduler trigger];
-
         [self processOutgoingQueue:operationGroup];
 
-        return true;
+        return CKKSDatabaseTransactionCommit;
     }];
 }
 
--(void)setCurrentItemForAccessGroup:(SecDbItemRef)newItem
+-(void)setCurrentItemForAccessGroup:(NSData* _Nonnull)newItemPersistentRef
                                hash:(NSData*)newItemSHA1
                         accessGroup:(NSString*)accessGroup
                          identifier:(NSString*)identifier
-                          replacing:(SecDbItemRef)oldItem
+                          replacing:(NSData* _Nullable)oldCurrentItemPersistentRef
                                hash:(NSData*)oldItemSHA1
                            complete:(void (^) (NSError* operror)) complete
 {
     if(accessGroup == nil || identifier == nil) {
-        NSError* error = [NSError errorWithDomain:@"securityd" code:errSecParam userInfo:@{NSLocalizedDescriptionKey: @"No access group or identifier given"}];
+        NSError* error = [NSError errorWithDomain:CKKSErrorDomain
+                                             code:errSecParam
+                                      description:@"No access group or identifier given"];
         ckkserror("ckkscurrent", self, "Cancelling request: %@", error);
         complete(error);
         return;
     }
 
-    __weak __typeof(self) weakSelf = self;
-
-    [self dispatchSync:^bool {
-        NSError* error = nil;
-        CFErrorRef cferror = NULL;
-
-        NSString* newItemUUID = nil;
-        NSString* oldItemUUID = nil;
-
-        // Now that we're on the db queue, ensure that the given hashes for the items match the hashes as they are now.
-        // That is, the items haven't changed since the caller knew about the item.
-        NSData* newItemComputedSHA1 = (NSData*) CFBridgingRelease(CFRetainSafe(SecDbItemGetSHA1(newItem, &cferror)));
-        if(!newItemComputedSHA1 || cferror ||
-           ![newItemComputedSHA1 isEqual:newItemSHA1]) {
-            ckksnotice("ckkscurrent", self, "Hash mismatch for new item: %@ vs %@", newItemComputedSHA1, newItemSHA1);
-            error = [NSError errorWithDomain:@"securityd" code:errSecItemInvalidValue userInfo:@{NSLocalizedDescriptionKey: @"New item has changed; hashes mismatch. Refetch and try again."}];
-            complete(error);
-            CFReleaseNull(cferror);
-            return false;
-        }
+    // Not being in a CloudKit account is an automatic failure.
+    // But, wait a good long while for the CloudKit account state to be known (in the case of daemon startup)
+    [self.accountStateKnown wait:(SecCKKSTestsEnabled() ? 1*NSEC_PER_SEC : 30*NSEC_PER_SEC)];
 
-        newItemUUID = (NSString*) CFBridgingRelease(CFRetainSafe(SecDbItemGetValue(newItem, &v10itemuuid, &cferror)));
-        if(!newItemUUID || cferror) {
-            ckkserror("ckkscurrent", self, "Error fetching UUID for new item: %@", cferror);
-            complete((__bridge NSError*) cferror);
-            CFReleaseNull(cferror);
-            return false;
-        }
+    if(self.accountStatus != CKKSAccountStatusAvailable) {
+        NSError* error = [NSError errorWithDomain:CKKSErrorDomain
+                                             code:CKKSNotLoggedIn
+                                      description:@"User is not signed into iCloud."];
+        ckksnotice("ckkscurrent", self, "Rejecting current item pointer set since we don't have an iCloud account.");
+        complete(error);
+        return;
+    }
 
-        if(oldItem) {
-            NSData* oldItemComputedSHA1 = (NSData*) CFBridgingRelease(CFRetainSafe(SecDbItemGetSHA1(oldItem, &cferror)));
-            if(!oldItemComputedSHA1 || cferror ||
-               ![oldItemComputedSHA1 isEqual:oldItemSHA1]) {
-                ckksnotice("ckkscurrent", self, "Hash mismatch for old item: %@ vs %@", oldItemComputedSHA1, oldItemSHA1);
-                error = [NSError errorWithDomain:@"securityd" code:errSecItemInvalidValue userInfo:@{NSLocalizedDescriptionKey: @"Old item has changed; hashes mismatch. Refetch and try again."}];
-                complete(error);
-                CFReleaseNull(cferror);
-                return false;
-            }
+    ckksnotice("ckkscurrent", self, "Starting change current pointer operation for %@-%@", accessGroup, identifier);
+    CKKSUpdateCurrentItemPointerOperation* ucipo = [[CKKSUpdateCurrentItemPointerOperation alloc] initWithCKKSKeychainView:self
+                                                                                                                   newItem:newItemPersistentRef
+                                                                                                                      hash:newItemSHA1
+                                                                                                               accessGroup:accessGroup
+                                                                                                                identifier:identifier
+                                                                                                                 replacing:oldCurrentItemPersistentRef
+                                                                                                                      hash:oldItemSHA1
+                                                                                                          ckoperationGroup:[CKOperationGroup CKKSGroupWithName:@"currentitem-api"]];
 
-            oldItemUUID = (NSString*) CFBridgingRelease(CFRetainSafe(SecDbItemGetValue(oldItem, &v10itemuuid, &cferror)));
-            if(!oldItemUUID || cferror) {
-                ckkserror("ckkscurrent", self, "Error fetching UUID for old item: %@", cferror);
-                complete((__bridge NSError*) cferror);
-                CFReleaseNull(cferror);
-                return false;
-            }
-        }
+    WEAKIFY(self);
+    CKKSResultOperation* returnCallback = [CKKSResultOperation operationWithBlock:^{
+        STRONGIFY(self);
 
-        // Not being in a CloudKit account is an automatic failure.
-        if(self.accountStatus != CKKSAccountStatusAvailable) {
-            ckksnotice("ckkscurrent", self, "Rejecting current item pointer set since we don't have an iCloud account.");
-            error = [NSError errorWithDomain:@"securityd" code:errSecNotLoggedIn userInfo:@{NSLocalizedDescriptionKey: @"User is not signed into iCloud."}];
-            complete(error);
-            return false;
+        if(ucipo.error) {
+            ckkserror("ckkscurrent", self, "Failed setting a current item pointer for %@ with %@", ucipo.currentPointerIdentifier, ucipo.error);
+        } else {
+            ckksnotice("ckkscurrent", self, "Finished setting a current item pointer for %@", ucipo.currentPointerIdentifier);
         }
+        complete(ucipo.error);
+    }];
+    returnCallback.name = @"setCurrentItem-return-callback";
+    [returnCallback addDependency: ucipo];
+    [self scheduleOperation: returnCallback];
 
-        // At this point, we've completed all the checks we need for the SecDbItems. Try to launch this boat!
-        NSString* currentIdentifier = [NSString stringWithFormat:@"%@-%@", accessGroup, identifier];
-        ckksnotice("ckkscurrent", self, "Setting current pointer for %@ to %@ (from %@)", currentIdentifier, newItemUUID, oldItemUUID);
-        CKKSUpdateCurrentItemPointerOperation* ucipo = [[CKKSUpdateCurrentItemPointerOperation alloc] initWithCKKSKeychainView:self
-                                                                                                                currentPointer:(NSString*)currentIdentifier
-                                                                                                                   oldItemUUID:(NSString*)oldItemUUID
-                                                                                                                   newItemUUID:(NSString*)newItemUUID
-                                                                                                              ckoperationGroup:[CKOperationGroup CKKSGroupWithName:@"currentitem-api"]];
-        CKKSResultOperation* returnCallback = [CKKSResultOperation operationWithBlock:^{
-            __strong __typeof(self) strongSelf = weakSelf;
-
-            if(ucipo.error) {
-                ckkserror("ckkscurrent", strongSelf, "Failed setting a current item pointer with %@", ucipo.error);
-            } else {
-                ckksnotice("ckkscurrent", strongSelf, "Finished setting a current item pointer");
-            }
-            complete(ucipo.error);
-        }];
-        returnCallback.name = @"setCurrentItem-return-callback";
-        [returnCallback addDependency: ucipo];
-        [self scheduleOperation: returnCallback];
-
-        // Now, schedule ucipo. It modifies the CloudKit zone, so it should insert itself into the list of OutgoingQueueOperations.
-        // Then, we won't have simultaneous zone-modifying operations.
-        [ucipo linearDependencies:self.outgoingQueueOperations];
+    // Now, schedule ucipo. It modifies the CloudKit zone, so it should insert itself into the list of OutgoingQueueOperations.
+    // Then, we won't have simultaneous zone-modifying operations.
+    [ucipo linearDependencies:self.outgoingQueueOperations];
 
-        // If this operation hasn't started within 60 seconds, cancel it and return a "timed out" error.
-        [ucipo timeout:60*NSEC_PER_SEC];
+    // If this operation hasn't started within 60 seconds, cancel it and return a "timed out" error.
+    [ucipo timeout:60*NSEC_PER_SEC];
 
-        [self scheduleOperation:ucipo];
-        return true;
-    }];
+    [self scheduleOperation:ucipo];
     return;
 }
 
                            complete:(void (^) (NSString* uuid, NSError* operror)) complete
 {
     if(accessGroup == nil || identifier == nil) {
-        complete(NULL, [NSError errorWithDomain:@"securityd" code:errSecParam userInfo:@{NSLocalizedDescriptionKey: @"No access group or identifier given"}]);
+        ckksnotice("ckkscurrent", self, "Rejecting current item pointer get since no access group(%@) or identifier(%@) given", accessGroup, identifier);
+        complete(NULL, [NSError errorWithDomain:CKKSErrorDomain
+                                           code:errSecParam
+                                    description:@"No access group or identifier given"]);
         return;
     }
 
     // Not being in a CloudKit account is an automatic failure.
+    // But, wait a good long while for the CloudKit account state to be known (in the case of daemon startup)
+    [self.accountStateKnown wait:(SecCKKSTestsEnabled() ? 1*NSEC_PER_SEC : 30*NSEC_PER_SEC)];
+
     if(self.accountStatus != CKKSAccountStatusAvailable) {
         ckksnotice("ckkscurrent", self, "Rejecting current item pointer get since we don't have an iCloud account.");
-        complete(NULL, [NSError errorWithDomain:@"securityd" code:errSecNotLoggedIn userInfo:@{NSLocalizedDescriptionKey: @"User is not signed into iCloud."}]);
+        complete(NULL, [NSError errorWithDomain:CKKSErrorDomain
+                                           code:CKKSNotLoggedIn
+                                    description:@"User is not signed into iCloud."]);
         return;
     }
 
         fetchAndProcess = [self fetchAndProcessCKChanges:CKKSFetchBecauseCurrentItemFetchRequest];
     }
 
-    __weak __typeof(self) weakSelf = self;
-    CKKSResultOperation* getCurrentItem = [CKKSResultOperation operationWithBlock:^{
+    WEAKIFY(self);
+    CKKSResultOperation* getCurrentItem = [CKKSResultOperation named:@"get-current-item-pointer" withBlock:^{
         if(fetchAndProcess.error) {
+            ckksnotice("ckkscurrent", self, "Rejecting current item pointer get since fetch failed: %@", fetchAndProcess.error);
             complete(NULL, fetchAndProcess.error);
             return;
         }
 
-        __strong __typeof(self) strongSelf = weakSelf;
+        STRONGIFY(self);
 
-        [strongSelf dispatchSync: ^bool {
+        [self dispatchSyncWithReadOnlySQLTransaction:^{
             NSError* error = nil;
             NSString* currentIdentifier = [NSString stringWithFormat:@"%@-%@", accessGroup, identifier];
 
             CKKSCurrentItemPointer* cip = [CKKSCurrentItemPointer fromDatabase:currentIdentifier
                                                                          state:SecCKKSProcessedStateLocal
-                                                                        zoneID:strongSelf.zoneID
+                                                                        zoneID:self.zoneID
                                                                          error:&error];
             if(!cip || error) {
-                ckkserror("ckkscurrent", strongSelf, "No current item pointer for %@", currentIdentifier);
+                if([error.domain isEqualToString:@"securityd"] && error.code == errSecItemNotFound) {
+                    // This error is common and very, very noisy. Shorten it and don't log here (the framework should log for us)
+                    ckksinfo("ckkscurrent", self, "No current item pointer for %@", currentIdentifier);
+                    error = [NSError errorWithDomain:@"securityd" code:errSecItemNotFound description:[NSString stringWithFormat:@"No current item pointer found for %@", currentIdentifier]];
+                } else {
+                    ckkserror("ckkscurrent", self, "No current item pointer for %@", currentIdentifier);
+                }
                 complete(nil, error);
-                return false;
+                return;
             }
 
             if(!cip.currentItemUUID) {
-                ckkserror("ckkscurrent", strongSelf, "Current item pointer is empty %@", cip);
-                complete(nil, [NSError errorWithDomain:@"securityd"
+                ckkserror("ckkscurrent", self, "Current item pointer is empty %@", cip);
+                complete(nil, [NSError errorWithDomain:CKKSErrorDomain
                                                   code:errSecInternalError
-                                              userInfo:@{NSLocalizedDescriptionKey: @"Current item pointer is empty"}]);
-                return false;
+                                           description:@"Current item pointer is empty"]);
+                return;
             }
 
+            ckksinfo("ckkscurrent", self, "Retrieved current item pointer: %@", cip);
             complete(cip.currentItemUUID, NULL);
-
-            return true;
+            return;
         }];
     }];
-    getCurrentItem.name = @"get-current-item-pointer";
 
     [getCurrentItem addNullableDependency:fetchAndProcess];
     [self scheduleOperation: getCurrentItem];
 }
 
-- (CKKSKey*) keyForItem: (SecDbItemRef) item error: (NSError * __autoreleasing *) error {
-    CKKSKeyClass* class = nil;
+- (CKKSResultOperation<CKKSKeySetProviderOperationProtocol>*)findKeySet:(BOOL)refetchBeforeReturningKeySet
+{
+    __block CKKSResultOperation<CKKSKeySetProviderOperationProtocol>* keysetOp = nil;
+    __block BOOL moveFromWaitForTrust = NO;
 
-    NSString* protection = (__bridge NSString*)SecDbItemGetCachedValueWithName(item, kSecAttrAccessible);
-    if([protection isEqualToString: (__bridge NSString*)kSecAttrAccessibleWhenUnlocked]) {
-        class = SecCKKSKeyClassA;
-    } else if([protection isEqualToString: (__bridge NSString*)kSecAttrAccessibleAlways] ||
-              [protection isEqualToString: (__bridge NSString*)kSecAttrAccessibleAfterFirstUnlock]) {
-        class = SecCKKSKeyClassC;
-    } else {
-        ckkserror("ckks", self, "can't pick key class for protection %@: %@", protection, item);
-        if(error) {
-           *error =[NSError errorWithDomain:@"securityd"
-                                code:5
-                            userInfo:@{NSLocalizedDescriptionKey:
-                                           [NSString stringWithFormat:@"can't pick key class for protection %@: %@", protection, item]}];
+    [self dispatchSyncWithReadOnlySQLTransaction:^{
+        keysetOp = (CKKSProvideKeySetOperation*)[self findFirstPendingOperation:self.operationDependencies.keysetProviderOperations];
+        if(!keysetOp) {
+            keysetOp = [[CKKSProvideKeySetOperation alloc] initWithZoneName:self.zoneName];
+            [self.operationDependencies.keysetProviderOperations addObject:keysetOp];
+
+            // This is an abuse of operations: they should generally run when added to a queue, not wait, but this allows recipients to set timeouts
+            [self scheduleOperationWithoutDependencies:keysetOp];
         }
 
-        return nil;
+        if(refetchBeforeReturningKeySet) {
+            ckksnotice("ckks", self, "Refetch requested before returning key set!");
+
+            [self.stateMachine _onqueueHandleFlag:CKKSFlagFetchRequested];
+            [self.stateMachine _onqueueHandleFlag:CKKSFlagTLKCreationRequested];
+
+            if([self.stateMachine.currentState isEqualToString:SecCKKSZoneKeyStateWaitForTrust]) {
+                moveFromWaitForTrust = YES;
+            }
+            return;
+        }
+
+        CKKSCurrentKeySet* keyset = [CKKSCurrentKeySet loadForZone:self.zoneID];
+        if(keyset.currentTLKPointer.currentKeyUUID &&
+           (keyset.tlk.uuid ||
+            [self.stateMachine.currentState isEqualToString:SecCKKSZoneKeyStateWaitForTrust] ||
+            [self.stateMachine.currentState isEqualToString:SecCKKSZoneKeyStateWaitForTLK])) {
+            ckksnotice("ckks", self, "Already have keyset %@", keyset);
+
+            [keysetOp provideKeySet:keyset];
+            return;
+
+        } else if([self.stateMachine.currentState isEqualToString:SecCKKSZoneKeyStateWaitForTrust]) {
+            // No keyset exists, but we're in waitfortrust? Seems like a bug. Move us out of this state...
+
+            ckksnotice("ckks", self, "Received a keyset request in an odd state; forwarding to state machine");
+            [self.stateMachine _onqueueHandleFlag:CKKSFlagTLKCreationRequested];
+            moveFromWaitForTrust = YES;
+
+        } else {
+            // The key state machine will know what to do.
+            [self.stateMachine _onqueueHandleFlag:CKKSFlagTLKCreationRequested];
+        };
+    }];
+
+    if(moveFromWaitForTrust) {
+        [self.stateMachine handleExternalRequest:[[OctagonStateTransitionRequest alloc] init:@"fix-bug"
+                                                                                sourceStates:[NSSet setWithObject:SecCKKSZoneKeyStateWaitForTrust]
+                                                                                 serialQueue:self.queue
+                                                                                     timeout:5 * NSEC_PER_SEC
+                                                                                transitionOp:[OctagonStateTransitionOperation named:@"fix-bug"
+                                                                                                                           entering:SecCKKSZoneKeyStateWaitForTLKCreation]]];
     }
 
-    CKKSKey* key = [CKKSKey currentKeyForClass: class zoneID:self.zoneID error:error];
+    return keysetOp;
+}
 
-    // and make sure it's unwrapped.
-    if(![key ensureKeyLoaded:error]) {
-        return nil;
+- (void)receiveTLKUploadRecords:(NSArray<CKRecord*>*)records
+{
+    // First, filter for records matching this zone
+    NSMutableArray<CKRecord*>* zoneRecords = [NSMutableArray array];
+    for(CKRecord* record in records) {
+        if([record.recordID.zoneID isEqual:self.zoneID]) {
+            [zoneRecords addObject:record];
+        }
+    }
+
+    ckksnotice("ckkskey", self, "Received a set of %lu TLK upload records", (unsigned long)zoneRecords.count);
+
+    if(!zoneRecords || zoneRecords.count == 0) {
+        return;
     }
 
-    return key;
+    [self dispatchSyncWithSQLTransaction:^CKKSDatabaseTransactionResult{
+        for(CKRecord* record in zoneRecords) {
+            [self _onqueueCKRecordChanged:record resync:false];
+        }
+
+        [self.stateMachine _onqueueHandleFlag:CKKSFlagKeyStateTLKsUploaded];
+
+        return CKKSDatabaseTransactionCommit;
+    }];
+}
+
+- (BOOL)requiresTLKUpload
+{
+    __block BOOL requiresUpload = NO;
+    dispatch_sync(self.queue, ^{
+        // We want to return true only if we're in a state that immediately requires an upload.
+        if(([self.keyHierarchyState isEqualToString:SecCKKSZoneKeyStateWaitForTLKUpload] ||
+            [self.keyHierarchyState isEqualToString:SecCKKSZoneKeyStateWaitForTLKCreation])) {
+            requiresUpload = YES;
+        }
+    });
+
+    return requiresUpload;
 }
 
 // Use the following method to find the first pending operation in a weak collection
     }
 }
 
-// Use the following method to count the pending operations in a weak collection
-- (int64_t)countPendingOperations: (NSHashTable*) table {
-    @synchronized(table) {
-        int count = 0;
-        for(NSOperation* op in table) {
-            if(op != nil && !([op isExecuting] || [op isFinished])) {
-                count++;
-            }
-        }
-        return count;
-    }
-}
-
-- (CKKSOutgoingQueueOperation*)processOutgoingQueue:(CKOperationGroup*)ckoperationGroup {
+- (CKKSOutgoingQueueOperation*)processOutgoingQueue:(CKOperationGroup* _Nullable)ckoperationGroup {
     return [self processOutgoingQueueAfter:nil ckoperationGroup:ckoperationGroup];
 }
 
-- (CKKSOutgoingQueueOperation*)processOutgoingQueueAfter:(CKKSResultOperation*)after ckoperationGroup:(CKOperationGroup*)ckoperationGroup {
-    if(!SecCKKSIsEnabled()) {
-        ckksinfo("ckks", self, "Skipping processOutgoingQueue due to disabled CKKS");
-        return nil;
-    }
+- (CKKSOutgoingQueueOperation*)processOutgoingQueueAfter:(CKKSResultOperation* _Nullable)after
+                                        ckoperationGroup:(CKOperationGroup* _Nullable)ckoperationGroup {
+    return [self processOutgoingQueueAfter:after requiredDelay:DISPATCH_TIME_FOREVER ckoperationGroup:ckoperationGroup];
+}
 
+- (CKKSOutgoingQueueOperation*)processOutgoingQueueAfter:(CKKSResultOperation* _Nullable)after
+                                           requiredDelay:(uint64_t)requiredDelay
+                                        ckoperationGroup:(CKOperationGroup* _Nullable)ckoperationGroup
+{
     CKKSOutgoingQueueOperation* outgoingop =
             (CKKSOutgoingQueueOperation*) [self findFirstPendingOperation:self.outgoingQueueOperations
                                                                   ofClass:[CKKSOutgoingQueueOperation class]];
     if(outgoingop) {
-        ckksinfo("ckks", self, "Skipping processOutgoingQueue due to at least one pending instance");
         if(after) {
             [outgoingop addDependency: after];
         }
             if(!outgoingop.ckoperationGroup && ckoperationGroup) {
                 outgoingop.ckoperationGroup = ckoperationGroup;
             } else if(ckoperationGroup) {
-                ckkserror("ckks", self, "Throwing away CKOperationGroup(%@) in favor of %@", ckoperationGroup, outgoingop.ckoperationGroup);
+                ckkserror("ckks", self, "Throwing away CKOperationGroup(%@) in favor of (%@)", ckoperationGroup.name, outgoingop.ckoperationGroup.name);
             }
 
+            // Will log any pending dependencies as well
+            ckksinfo("ckksoutgoing", self, "Returning existing %@", outgoingop);
+
+            // Shouldn't be necessary, but can't hurt
+            [self.outgoingQueueOperationScheduler triggerAt:requiredDelay];
             return outgoingop;
         }
     }
 
-    CKKSOutgoingQueueOperation* op = [[CKKSOutgoingQueueOperation alloc] initWithCKKSKeychainView:self ckoperationGroup:ckoperationGroup];
+    CKKSOutgoingQueueOperation* op = [[CKKSOutgoingQueueOperation alloc] initWithDependencies:self.operationDependencies
+                                                                                         ckks:self
+                                                                                    intending:SecCKKSZoneKeyStateReady
+                                                                                   errorState:SecCKKSZoneKeyStateUnhealthy
+                                                                             ckoperationGroup:ckoperationGroup];
     op.name = @"outgoing-queue-operation";
     [op addNullableDependency:after];
+    [op addNullableDependency:self.outgoingQueueOperationScheduler.operationDependency];
 
-    [op addNullableDependency: self.initialScanOperation];
+    [self.outgoingQueueOperationScheduler triggerAt:requiredDelay];
+
+    [op linearDependencies:self.outgoingQueueOperations];
 
     [self scheduleOperation: op];
+    ckksnotice("ckksoutgoing", self, "Scheduled %@", op);
     return op;
 }
 
 - (void)processIncomingQueueAfterNextUnlock {
     // Thread races aren't so important here; we might end up with two or three copies of this operation, but that's okay.
     if(![self.processIncomingQueueAfterNextUnlockOperation isPending]) {
-        __weak __typeof(self) weakSelf = self;
+        WEAKIFY(self);
 
         CKKSResultOperation* restartIncomingQueueOperation = [CKKSResultOperation operationWithBlock:^{
-            __strong __typeof(self) strongSelf = weakSelf;
+            STRONGIFY(self);
             // This IQO shouldn't error if the keybag has locked again. It will simply try again later.
-            [strongSelf processIncomingQueue:false];
+            [self processIncomingQueue:false];
         }];
 
         restartIncomingQueueOperation.name = @"reprocess-incoming-queue-after-unlock";
     }
 }
 
+- (CKKSResultOperation*)resultsOfNextProcessIncomingQueueOperation {
+    if(self.resultsOfNextIncomingQueueOperationOperation && [self.resultsOfNextIncomingQueueOperationOperation isPending]) {
+        return self.resultsOfNextIncomingQueueOperationOperation;
+    }
+
+    // Else, make a new one.
+    self.resultsOfNextIncomingQueueOperationOperation = [CKKSResultOperation named:[NSString stringWithFormat:@"wait-for-next-incoming-queue-operation-%@", self.zoneName] withBlock:^{}];
+    return self.resultsOfNextIncomingQueueOperationOperation;
+}
+
 - (CKKSIncomingQueueOperation*)processIncomingQueue:(bool)failOnClassA {
     return [self processIncomingQueue:failOnClassA after: nil];
 }
 
 - (CKKSIncomingQueueOperation*) processIncomingQueue:(bool)failOnClassA after: (CKKSResultOperation*) after {
-    if(!SecCKKSIsEnabled()) {
-        ckksinfo("ckks", self, "Skipping processIncomingQueue due to disabled CKKS");
-        return nil;
-    }
+    return [self processIncomingQueue:failOnClassA after:after policyConsideredAuthoritative:false];
+}
 
+- (CKKSIncomingQueueOperation*)processIncomingQueue:(bool)failOnClassA
+                                              after:(CKKSResultOperation*)after
+                      policyConsideredAuthoritative:(bool)policyConsideredAuthoritative
+{
     CKKSIncomingQueueOperation* incomingop = (CKKSIncomingQueueOperation*) [self findFirstPendingOperation:self.incomingQueueOperations];
     if(incomingop) {
         ckksinfo("ckks", self, "Skipping processIncomingQueue due to at least one pending instance");
         if(after) {
-            [incomingop addDependency: after];
+            [incomingop addNullableDependency: after];
         }
+
         // check (again) for race condition; if the op has started we need to add another (for the dependency)
         if([incomingop isPending]) {
             incomingop.errorOnClassAFailure |= failOnClassA;
+            incomingop.handleMismatchedViewItems |= policyConsideredAuthoritative;
             return incomingop;
         }
     }
 
-    CKKSIncomingQueueOperation* op  = [[CKKSIncomingQueueOperation alloc] initWithCKKSKeychainView:self errorOnClassAFailure:failOnClassA];
+    CKKSIncomingQueueOperation* op = [[CKKSIncomingQueueOperation alloc] initWithDependencies:self.operationDependencies
+                                                                                         ckks:self
+                                                                                    intending:SecCKKSZoneKeyStateReady
+                                                                                   errorState:SecCKKSZoneKeyStateUnhealthy
+                                                                         errorOnClassAFailure:failOnClassA
+                                                                        handleMismatchedViewItems:policyConsideredAuthoritative];
     op.name = @"incoming-queue-operation";
     if(after != nil) {
         [op addSuccessDependency: after];
     }
 
+    if(self.resultsOfNextIncomingQueueOperationOperation) {
+        [self.resultsOfNextIncomingQueueOperationOperation addSuccessDependency:op];
+        [self scheduleOperation:self.resultsOfNextIncomingQueueOperationOperation];
+        self.resultsOfNextIncomingQueueOperationOperation = nil;
+    }
+
     [self scheduleOperation: op];
     return op;
 }
 
-- (CKKSUpdateDeviceStateOperation*)updateDeviceState:(bool)rateLimit ckoperationGroup:(CKOperationGroup*)ckoperationGroup {
-    if(!SecCKKSIsEnabled()) {
-        ckksinfo("ckks", self, "Skipping updateDeviceState due to disabled CKKS");
-        return nil;
+- (CKKSScanLocalItemsOperation*)scanLocalItems:(NSString*)operationName {
+    return [self scanLocalItems:operationName ckoperationGroup:nil after:nil];
+}
+
+- (CKKSScanLocalItemsOperation*)scanLocalItems:(NSString*)operationName
+                              ckoperationGroup:(CKOperationGroup*)operationGroup
+                                         after:(NSOperation*)after
+{
+    CKKSScanLocalItemsOperation* scanOperation = (CKKSScanLocalItemsOperation*)[self findFirstPendingOperation:self.scanLocalItemsOperations];
+
+    if(scanOperation) {
+        [scanOperation addNullableDependency:after];
+
+        // check (again) for race condition; if the op has started we need to add another (for the dependency)
+        if([scanOperation isPending]) {
+            scanOperation.ckoperationGroup = operationGroup;
+
+            scanOperation.name = [NSString stringWithFormat:@"%@::%@", scanOperation.name, operationName];
+            return scanOperation;
+        }
     }
 
+    scanOperation = [[CKKSScanLocalItemsOperation alloc] initWithDependencies:self.operationDependencies
+                                                                         ckks:self
+                                                                    intending:SecCKKSZoneKeyStateReady
+                                                                   errorState:SecCKKSZoneKeyStateError
+                                                             ckoperationGroup:operationGroup];
+    scanOperation.name = operationName;
+
+    [scanOperation addNullableDependency:self.lastFixupOperation];
+    [scanOperation addNullableDependency:self.lockStateTracker.unlockDependency];
+    [scanOperation addNullableDependency:self.keyStateReadyDependency];
+    [scanOperation addNullableDependency:after];
+
+    [scanOperation linearDependencies:self.scanLocalItemsOperations];
+
+    // This might generate items for upload. Make sure that any uploads wait until the scan is complete, so we know what to upload
+    [scanOperation linearDependencies:self.outgoingQueueOperations];
+
+    [self scheduleOperation:scanOperation];
+    self.initiatedLocalScan = YES;
+    return scanOperation;
+}
+
+- (CKKSUpdateDeviceStateOperation*)updateDeviceState:(bool)rateLimit
+                   waitForKeyHierarchyInitialization:(uint64_t)timeout
+                                    ckoperationGroup:(CKOperationGroup*)ckoperationGroup {
+    // If securityd just started, the key state might be in some transient early state. Wait a bit.
+    OctagonStateMultiStateArrivalWatcher* waitForTransient = [[OctagonStateMultiStateArrivalWatcher alloc] initNamed:@"rpc-watcher"
+                                                                                                         serialQueue:self.queue
+                                                                                                              states:CKKSKeyStateNonTransientStates()];
+    [waitForTransient timeout:timeout];
+    [self.stateMachine registerMultiStateArrivalWatcher:waitForTransient];
+
     CKKSUpdateDeviceStateOperation* op = [[CKKSUpdateDeviceStateOperation alloc] initWithCKKSKeychainView:self rateLimit:rateLimit ckoperationGroup:ckoperationGroup];
     op.name = @"device-state-operation";
 
+    [op addDependency:waitForTransient.result];
+
     // op modifies the CloudKit zone, so it should insert itself into the list of OutgoingQueueOperations.
     // Then, we won't have simultaneous zone-modifying operations and confuse ourselves.
     // However, since we might have pending OQOs, it should try to insert itself at the beginning of the linearized list
     [op linearDependenciesWithSelfFirst:self.outgoingQueueOperations];
 
     // CKKSUpdateDeviceStateOperations are special: they should fire even if we don't believe we're in an iCloud account.
-    [self scheduleAccountStatusOperation:op];
+    // They also shouldn't block or be blocked by any other operation; our wait operation above will handle that
+    [self scheduleOperationWithoutDependencies:op];
     return op;
 }
 
+- (void)xpc24HrNotification
+{
+    // Called roughly once every 24hrs
+    [self.stateMachine handleFlag:CKKSFlag24hrNotification];
+}
+
 // There are some errors which won't be reported but will be reflected in the CDSE; any error coming out of here is fatal
 - (CKKSDeviceStateEntry*)_onqueueCurrentDeviceStateEntry: (NSError* __autoreleasing*)error {
+    dispatch_assert_queue(self.queue);
     NSError* localerror = nil;
 
-    CKKSCKAccountStateTracker* accountTracker = self.accountTracker;
+    CKKSAccountStateTracker* accountTracker = self.accountTracker;
+    CKKSAccountStatus hsa2Status = accountTracker.hsa2iCloudAccountStatus;
 
-    // We must have an iCloud account (with d2de on) to even create one of these
-    if(accountTracker.currentCKAccountInfo.accountStatus != CKAccountStatusAvailable || accountTracker.currentCKAccountInfo.supportsDeviceToDeviceEncryption != YES) {
-        ckkserror("ckksdevice", self, "No iCloud account active: %@", accountTracker.currentCKAccountInfo);
+    // We must have an HSA2 iCloud account and a CloudKit account to even create one of these
+    if(hsa2Status != CKKSAccountStatusAvailable ||
+       accountTracker.currentCKAccountInfo.accountStatus != CKAccountStatusAvailable) {
+        ckkserror("ckksdevice", self, "No iCloud account active: %@ hsa2 account:%@",
+                  accountTracker.currentCKAccountInfo,
+                  CKKSAccountStatusToString(hsa2Status));
         localerror = [NSError errorWithDomain:@"securityd"
                                          code:errSecInternalError
                                      userInfo:@{NSLocalizedDescriptionKey: [NSString stringWithFormat: @"No active HSA2 iCloud account: %@", accountTracker.currentCKAccountInfo]}];
         return nil;
     }
 
-    CKKSDeviceStateEntry* oldcdse = [CKKSDeviceStateEntry tryFromDatabase:accountTracker.ckdeviceID zoneID:self.zoneID error:&localerror];
-    if(localerror) {
-        ckkserror("ckksdevice", self, "Couldn't read old CKKSDeviceStateEntry from database: %@", localerror);
+    NSString* ckdeviceID = accountTracker.ckdeviceID;
+    if(ckdeviceID == nil) {
+        ckkserror("ckksdevice", self, "No CK device ID available; cannot make device state entry");
+        localerror = [NSError errorWithDomain:CKKSErrorDomain
+                                         code:CKKSNotLoggedIn
+                                     userInfo:@{NSLocalizedDescriptionKey: [NSString stringWithFormat: @"No CK device ID: %@", accountTracker.currentCKAccountInfo]}];
         if(error) {
             *error = localerror;
         }
         return nil;
     }
 
-    // Find out what we think the current keys are
+    CKKSDeviceStateEntry* oldcdse = [CKKSDeviceStateEntry tryFromDatabase:ckdeviceID zoneID:self.zoneID error:&localerror];
+    if(localerror) {
+        ckkserror("ckksdevice", self, "Couldn't read old CKKSDeviceStateEntry from database: %@", localerror);
+        if(error) {
+            *error = localerror;
+        }
+        return nil;
+    }
+
+    // Find out what we think the current keys are
     CKKSCurrentKeyPointer* currentTLKPointer    = [CKKSCurrentKeyPointer tryFromDatabase: SecCKKSKeyClassTLK zoneID:self.zoneID error:&localerror];
     CKKSCurrentKeyPointer* currentClassAPointer = [CKKSCurrentKeyPointer tryFromDatabase: SecCKKSKeyClassA   zoneID:self.zoneID error:&localerror];
     CKKSCurrentKeyPointer* currentClassCPointer = [CKKSCurrentKeyPointer tryFromDatabase: SecCKKSKeyClassC   zoneID:self.zoneID error:&localerror];
     }
 
     // We'd like to have the circle peer ID. Give the account state tracker a fighting chance, but not having it is not an error
-    if([accountTracker.accountCirclePeerIDInitialized wait:500*NSEC_PER_MSEC] != 0 && !accountTracker.accountCirclePeerID) {
-        ckkserror("ckksdevice", self, "No peer ID available");
+    // But, if the platform doesn't have SOS, don't bother
+    if(OctagonPlatformSupportsSOS() && [accountTracker.accountCirclePeerIDInitialized wait:500*NSEC_PER_MSEC] != 0 && !accountTracker.accountCirclePeerID) {
+        ckkserror("ckksdevice", self, "No SOS peer ID available");
+    }
+
+    // We'd also like the Octagon status
+    if([accountTracker.octagonInformationInitialized wait:500*NSEC_PER_MSEC] != 0 && !accountTracker.octagonPeerID) {
+        ckkserror("ckksdevice", self, "No octagon peer ID available");
     }
 
+    // Reset the last unlock time to 'day' granularity in UTC
+    NSCalendar* calendar = [NSCalendar calendarWithIdentifier:NSCalendarIdentifierISO8601];
+    calendar.timeZone = [NSTimeZone timeZoneWithAbbreviation:@"UTC"];
+    NSDate* lastUnlockDay = self.lockStateTracker.lastUnlockTime;
+    lastUnlockDay = lastUnlockDay ? [calendar startOfDayForDate:lastUnlockDay] : nil;
+
     // We only really want the oldcdse for its encodedCKRecord, so make a new cdse here
-    CKKSDeviceStateEntry* newcdse = [[CKKSDeviceStateEntry alloc] initForDevice:accountTracker.ckdeviceID
+    CKKSDeviceStateEntry* newcdse = [[CKKSDeviceStateEntry alloc] initForDevice:ckdeviceID
+                                                                      osVersion:SecCKKSHostOSVersion()
+                                                                 lastUnlockTime:lastUnlockDay
+                                                                  octagonPeerID:accountTracker.octagonPeerID
+                                                                  octagonStatus:accountTracker.octagonStatus
                                                                    circlePeerID:accountTracker.accountCirclePeerID
-                                                                   circleStatus:accountTracker.currentCircleStatus
+                                                                   circleStatus:accountTracker.currentCircleStatus.status
                                                                        keyState:self.keyHierarchyState
                                                                  currentTLKUUID:suggestedTLK.uuid
                                                               currentClassAUUID:suggestedClassAKey.uuid
 }
 
 - (CKKSSynchronizeOperation*) resyncWithCloud {
-    if(!SecCKKSIsEnabled()) {
-        ckksinfo("ckks", self, "Skipping resyncWithCloud due to disabled CKKS");
-        return nil;
-    }
-
     CKKSSynchronizeOperation* op = [[CKKSSynchronizeOperation alloc] initWithCKKSKeychainView: self];
     [self scheduleOperation: op];
     return op;
 }
 
-- (CKKSResultOperation*)fetchAndProcessCKChanges:(CKKSFetchBecause*)because {
-    if(!SecCKKSIsEnabled()) {
+- (CKKSLocalSynchronizeOperation*)resyncLocal {
+    CKKSLocalSynchronizeOperation* op = [[CKKSLocalSynchronizeOperation alloc] initWithCKKSKeychainView:self];
+    [self scheduleOperation: op];
+    return op;
+}
+
+- (CKKSResultOperation*)fetchAndProcessCKChanges:(CKKSFetchBecause*)because
+{
+     if(!SecCKKSIsEnabled()) {
         ckksinfo("ckks", self, "Skipping fetchAndProcessCKChanges due to disabled CKKS");
         return nil;
     }
 
-    // We fetched some changes; try to process them!
+     // We fetched some changes; try to process them!
     return [self processIncomingQueue:false after:[self.zoneChangeFetcher requestSuccessfulFetch:because]];
 }
 
                 return true;
             }
         }
+
+        // Check if this error was the CKKS server extension rejecting the write
+        for(CKRecordID* recordID in partialErrors.allKeys) {
+            NSError* error = partialErrors[recordID];
+
+            NSError* underlyingError = error.userInfo[NSUnderlyingErrorKey];
+            NSError* thirdLevelError = underlyingError.userInfo[NSUnderlyingErrorKey];
+            ckksnotice("ckks", self, "Examining 'write failed' error: %@ %@ %@", error, underlyingError, thirdLevelError);
+
+            if([error.domain isEqualToString:CKErrorDomain] && error.code == CKErrorServerRejectedRequest &&
+               underlyingError && [underlyingError.domain isEqualToString:CKInternalErrorDomain] && underlyingError.code == CKErrorInternalPluginError &&
+               thirdLevelError && [thirdLevelError.domain isEqualToString:@"CloudkitKeychainService"]) {
+
+                if(thirdLevelError.code == CKKSServerUnexpectedSyncKeyInChain) {
+                    // The server thinks the classA/C synckeys don't wrap directly the to top TLK, but we don't (otherwise, we would have fixed it).
+                    // Issue a key hierarchy fetch and see what's what.
+                    ckkserror("ckks", self, "CKKS Server extension has told us about %@ for record %@; requesting refetch and reprocess of key hierarchy", thirdLevelError, recordID);
+                    [self.stateMachine _onqueueHandleFlag:CKKSFlagFetchRequested];
+
+                } else if(thirdLevelError.code == CKKSServerMissingRecord) {
+                    // The server is concerned that there's a missing record somewhere.
+                    // Issue a key hierarchy fetch and see what's happening
+                    ckkserror("ckks", self, "CKKS Server extension has told us about %@ for record %@; requesting refetch and reprocess of key hierarchy", thirdLevelError, recordID);
+                    [self.stateMachine _onqueueHandleFlag:CKKSFlagFetchRequested];
+
+                } else {
+                    ckkserror("ckks", self, "CKKS Server extension has told us about %@ for record %@, but we don't currently handle this error", thirdLevelError, recordID);
+                }
+            }
+        }
     }
 
     return false;
     // TODO: resync doesn't really mean much here; what does it mean for a record to be 'deleted' if you're fetching from scratch?
 
     if([recordType isEqual: SecCKRecordItemType]) {
-        ckksinfo("ckks", self, "CloudKit notification: deleted record(%@): %@", recordType, recordID);
+        ckksnotice("ckks", self, "CloudKit notification: deleted record(%@): %@", recordType, recordID);
         NSError* error = nil;
         NSError* iqeerror = nil;
         CKKSMirrorEntry* ckme = [CKKSMirrorEntry fromDatabase: [recordID recordName] zoneID:self.zoneID error: &error];
             if(iqeerror) {
                 ckkserror("ckks", self, "Couldn't save incoming queue entry: %@", iqeerror);
             }
+
+            // Delete any pending local changes; this delete wins
+            NSArray<CKKSOutgoingQueueEntry*>* siblings = [CKKSOutgoingQueueEntry allWithUUID:iqe.uuid
+                                                                                      states:@[SecCKKSStateNew,
+                                                                                               SecCKKSStateReencrypt,
+                                                                                               SecCKKSStateError]
+                                                                                      zoneID:self.zoneID
+                                                                                       error:&error];
+            if(error) {
+                ckkserror("ckks", self, "Couldn't load OQE sibling for %@: %@", iqe.uuid, error);
+            }
+
+            for(CKKSOutgoingQueueEntry* oqe in siblings) {
+                NSError* deletionError = nil;
+                [oqe deleteFromDatabase:&deletionError];
+                if(deletionError) {
+                    ckkserror("ckks", self, "Couldn't delete OQE sibling(%@) for %@: %@", oqe, iqe.uuid, deletionError);
+                }
+            }
         }
         ckksinfo("ckks", self, "CKKSMirrorEntry was deleted: %@ %@ error: %@", recordID, ckme, error);
         // TODO: actually pass error back up
     } else if([recordType isEqual: SecCKRecordIntermediateKeyType]) {
         // TODO: handle in some interesting way
         return true;
+    } else if([recordType isEqual: SecCKRecordTLKShareType]) {
+        NSError* error = nil;
+        ckksinfo("ckks", self, "CloudKit notification: deleted tlk share record(%@): %@", recordType, recordID);
+        CKKSTLKShareRecord* share = [CKKSTLKShareRecord tryFromDatabaseFromCKRecordID:recordID error:&error];
+        [share deleteFromDatabase:&error];
+
+        if(error) {
+            ckkserror("ckks", self, "CK notification: Couldn't delete deleted TLKShare: %@ %@", recordID,  error);
+        }
+        return (error == nil);
+
     } else if([recordType isEqual: SecCKRecordDeviceStateType]) {
         NSError* error = nil;
         ckksinfo("ckks", self, "CloudKit notification: deleted device state record(%@): %@", recordType, recordID);
         // TODO: actually pass error back up
         return error == nil;
     }
+
     else {
         ckkserror("ckksfetch", self, "unknown record type: %@ %@", recordType, recordID);
         return false;
 - (bool)_onqueueCKRecordChanged:(CKRecord*)record resync:(bool)resync {
     dispatch_assert_queue(self.queue);
 
-    ckksinfo("ckksfetch", self, "Processing record modification(%@): %@", record.recordType, record);
+    @autoreleasepool {
+        ckksnotice("ckksfetch", self, "Processing record modification(%@): %@", record.recordType, record);
 
-    if([[record recordType] isEqual: SecCKRecordItemType]) {
-        [self _onqueueCKRecordItemChanged:record resync:resync];
-        return true;
-    } else if([[record recordType] isEqual: SecCKRecordCurrentItemType]) {
-        [self _onqueueCKRecordCurrentItemPointerChanged:record resync:resync];
-        return true;
-    } else if([[record recordType] isEqual: SecCKRecordIntermediateKeyType]) {
-        [self _onqueueCKRecordKeyChanged:record resync:resync];
-        return true;
-    } else if([[record recordType] isEqualToString: SecCKRecordCurrentKeyType]) {
-        [self _onqueueCKRecordCurrentKeyPointerChanged:record resync:resync];
-        return true;
-    } else if ([[record recordType] isEqualToString:SecCKRecordManifestType]) {
-        [self _onqueueCKRecordManifestChanged:record resync:resync];
-        return true;
-    } else if ([[record recordType] isEqualToString:SecCKRecordManifestLeafType]) {
-        [self _onqueueCKRecordManifestLeafChanged:record resync:resync];
-        return true;
-    } else if ([[record recordType] isEqualToString:SecCKRecordDeviceStateType]) {
-        [self _onqueueCKRecordDeviceStateChanged:record resync:resync];
-        return true;
-    } else {
-        ckkserror("ckksfetch", self, "unknown record type: %@ %@", [record recordType], record);
-        return false;
+        if([[record recordType] isEqual: SecCKRecordItemType]) {
+            [self _onqueueCKRecordItemChanged:record resync:resync];
+            return true;
+        } else if([[record recordType] isEqual: SecCKRecordCurrentItemType]) {
+            [self _onqueueCKRecordCurrentItemPointerChanged:record resync:resync];
+            return true;
+        } else if([[record recordType] isEqual: SecCKRecordIntermediateKeyType]) {
+            [self _onqueueCKRecordKeyChanged:record resync:resync];
+            return true;
+        } else if ([[record recordType] isEqual: SecCKRecordTLKShareType]) {
+            [self _onqueueCKRecordTLKShareChanged:record resync:resync];
+            return true;
+        } else if([[record recordType] isEqualToString: SecCKRecordCurrentKeyType]) {
+            [self _onqueueCKRecordCurrentKeyPointerChanged:record resync:resync];
+            return true;
+        } else if ([[record recordType] isEqualToString:SecCKRecordManifestType]) {
+            [self _onqueueCKRecordManifestChanged:record resync:resync];
+            return true;
+        } else if ([[record recordType] isEqualToString:SecCKRecordManifestLeafType]) {
+            [self _onqueueCKRecordManifestLeafChanged:record resync:resync];
+            return true;
+        } else if ([[record recordType] isEqualToString:SecCKRecordDeviceStateType]) {
+            [self _onqueueCKRecordDeviceStateChanged:record resync:resync];
+            return true;
+        } else {
+            ckkserror("ckksfetch", self, "unknown record type: %@ %@", [record recordType], record);
+            return false;
+        }
     }
 }
 
         } else if(![ckme matchesCKRecord:record]) {
             ckkserror("ckksresync", self, "BUG: Local item doesn't match resynced CloudKit record: %@ %@", ckme, record);
         } else {
-            ckksnotice("ckksresync", self, "Already know about this item record, skipping update: %@", record);
-            return;
+            ckksnotice("ckksresync", self, "Already know about this item record, updating anyway: %@", record.recordID);
         }
     }
 
 
     // If we found an old version in the database; this might be an update
     if(ckme) {
-        if([ckme matchesCKRecord:record]) {
+        if([ckme matchesCKRecord:record] && !resync) {
             // This is almost certainly a record we uploaded; CKFetchChanges sends them back as new records
-            ckksnotice("ckks", self, "CloudKit has told us of record we already know about; skipping update");
+            ckksnotice("ckks", self, "CloudKit has told us of record we already know about for %@; skipping update", ckme.uuid);
             return;
         }
 
     if(error) {
         ckkserror("ckks", self, "couldn't save new CKRecord to database: %@ %@", record, error);
     } else {
-        ckksdebug("ckks", self, "CKKSMirrorEntry was created: %@", ckme);
+        ckksinfo("ckks", self, "CKKSMirrorEntry was created: %@", ckme);
     }
 
     NSError* iqeerror = nil;
     if(iqeerror) {
         ckkserror("ckks", self, "Couldn't save modified incoming queue entry: %@", iqeerror);
     } else {
-        ckksdebug("ckks", self, "CKKSIncomingQueueEntry was created: %@", iqe);
+        ckksinfo("ckks", self, "CKKSIncomingQueueEntry was created: %@", iqe);
     }
 
     // A remote change has occured for this record. Delete any pending local changes; they will be overwritten.
-    CKKSOutgoingQueueEntry* oqe = [CKKSOutgoingQueueEntry tryFromDatabase:ckme.uuid state: SecCKKSStateNew zoneID:self.zoneID error: &error];
+    NSArray<CKKSOutgoingQueueEntry*>* siblings = [CKKSOutgoingQueueEntry allWithUUID:iqe.uuid
+                                                                              states:@[SecCKKSStateNew,
+                                                                                       SecCKKSStateReencrypt,
+                                                                                       SecCKKSStateError]
+                                                                              zoneID:self.zoneID
+                                                                               error:&error];
     if(error) {
-        ckkserror("ckks", self, "Couldn't load OutgoingQueueEntry: %@", error);
-    }
-    if(oqe) {
-        [self _onqueueChangeOutgoingQueueEntry:oqe toState:SecCKKSStateDeleted error:&error];
+        ckkserror("ckks", self, "Couldn't load OQE sibling for %@: %@", iqe.uuid, error);
     }
 
-    // Reencryptions are pending changes too
-    oqe = [CKKSOutgoingQueueEntry tryFromDatabase:ckme.uuid state: SecCKKSStateReencrypt zoneID:self.zoneID error: &error];
-    if(error) {
-        ckkserror("ckks", self, "Couldn't load reencrypted OutgoingQueueEntry: %@", error);
-    }
-    if(oqe) {
-        [oqe deleteFromDatabase:&error];
-        if(error) {
-            ckkserror("ckks", self, "Couldn't delete reencrypted oqe(%@): %@", oqe, error);
+    for(CKKSOutgoingQueueEntry* oqe in siblings) {
+        NSError* deletionError = nil;
+        [oqe deleteFromDatabase:&deletionError];
+        if(deletionError) {
+            ckkserror("ckks", self, "Couldn't delete OQE sibling(%@) for %@: %@", oqe, iqe.uuid, deletionError);
         }
     }
 }
         }
     }
 
-    // For now, drop into the synckeys table as a 'remote' key, then ask for a rekey operation.
     CKKSKey* remotekey = [[CKKSKey alloc] initWithCKRecord: record];
 
-    // We received this from an update. Don't use, yet.
+    // Do we already know about this key?
+    CKKSKey* possibleLocalKey = [CKKSKey tryFromDatabase:remotekey.uuid zoneID:self.zoneID error:&error];
+    if(error) {
+        ckkserror("ckkskey", self, "Error findibg exsiting local key for %@: %@", remotekey, error);
+        // Go on, assuming there isn't a local key
+    } else if(possibleLocalKey && [possibleLocalKey matchesCKRecord:record]) {
+        // Okay, nothing new here. Update the CKRecord and move on.
+        // Note: If the new record doesn't match the local copy, we have to go through the whole dance below
+        possibleLocalKey.storedCKRecord = record;
+        [possibleLocalKey saveToDatabase:&error];
+
+        if(error) {
+            ckkserror("ckkskey", self, "Couldn't update existing key: %@: %@", possibleLocalKey, error);
+        }
+        return;
+    }
+
+    // Drop into the synckeys table as a 'remote' key, then ask for a rekey operation.
     remotekey.state = SecCKKSProcessedStateRemote;
     remotekey.currentkey = false;
 
     }
 
     // We've saved a new key in the database; trigger a rekey operation.
-    [self _onqueueKeyStateMachineRequestProcess];
+    [self.stateMachine _onqueueHandleFlag:CKKSFlagKeyStateProcessRequested];
+}
+
+- (void)_onqueueCKRecordTLKShareChanged:(CKRecord*)record resync:(bool)resync {
+    dispatch_assert_queue(self.queue);
+
+    NSError* error = nil;
+    if(resync) {
+        // TODO fill in
+    }
+
+    // CKKSTLKShares get saved with no modification
+    CKKSTLKShareRecord* share = [[CKKSTLKShareRecord alloc] initWithCKRecord:record];
+    [share saveToDatabase:&error];
+    if(error) {
+        ckkserror("ckksshare", self, "Couldn't save new TLK share to database: %@ %@", share, error);
+    }
+
+    [self.stateMachine _onqueueHandleFlag:CKKSFlagKeyStateProcessRequested];
 }
 
 - (void)_onqueueCKRecordCurrentKeyPointerChanged:(CKRecord*)record resync:(bool)resync {
     dispatch_assert_queue(self.queue);
 
+    // Pull out the old CKP, if it exists
+    NSError* ckperror = nil;
+    CKKSCurrentKeyPointer* oldckp = [CKKSCurrentKeyPointer tryFromDatabase:((CKKSKeyClass*) record.recordID.recordName) zoneID:self.zoneID error:&ckperror];
+    if(ckperror) {
+        ckkserror("ckkskey", self, "error loading ckp: %@", ckperror);
+    }
+
     if(resync) {
-        NSError* ckperror = nil;
-        CKKSCurrentKeyPointer* ckp = [CKKSCurrentKeyPointer tryFromDatabase:((CKKSKeyClass*) record.recordID.recordName) zoneID:self.zoneID error:&ckperror];
-        if(ckperror) {
-            ckkserror("ckksresync", self, "error loading ckp: %@", ckperror);
-        }
-        if(!ckp) {
+        if(!oldckp) {
             ckkserror("ckksresync", self, "BUG: No current key pointer matching resynced CloudKit record: %@", record);
-        } else if(![ckp matchesCKRecord:record]) {
-            ckkserror("ckksresync", self, "BUG: Local current key pointer doesn't match resynced CloudKit record: %@ %@", ckp, record);
+        } else if(![oldckp matchesCKRecord:record]) {
+            ckkserror("ckksresync", self, "BUG: Local current key pointer doesn't match resynced CloudKit record: %@ %@", oldckp, record);
         } else {
-            ckksnotice("ckksresync", self, "Already know about this current key pointer, skipping update: %@", record);
-            return;
+            ckksnotice("ckksresync", self, "Current key pointer has 'changed', but it matches our local copy: %@", record);
         }
     }
 
         ckksinfo("ckkskey", self, "CKRecord was %@", record);
     }
 
-    // We've saved a new key in the database; trigger a rekey operation.
-    [self _onqueueKeyStateMachineRequestProcess];
+    if([oldckp matchesCKRecord:record]) {
+        ckksnotice("ckkskey", self, "Current key pointer modification doesn't change anything interesting; skipping reprocess: %@", record);
+    } else {
+        // We've saved a new key in the database; trigger a rekey operation.
+        [self.stateMachine _onqueueHandleFlag:CKKSFlagKeyStateProcessRequested];
+    }
 }
 
 - (void)_onqueueCKRecordCurrentItemPointerChanged:(CKRecord*)record resync:(bool)resync {
 
 - (void)_onqueueCKRecordManifestChanged:(CKRecord*)record resync:(bool)resync
 {
+    dispatch_assert_queue(self.queue);
     NSError* error = nil;
     CKKSPendingManifest* manifest = [[CKKSPendingManifest alloc] initWithCKRecord:record];
     [manifest saveToDatabase:&error];
 
 - (void)_onqueueCKRecordManifestLeafChanged:(CKRecord*)record resync:(bool)resync
 {
+    dispatch_assert_queue(self.queue);
     NSError* error = nil;
     CKKSManifestLeafRecord* manifestLeaf = [[CKKSManifestPendingLeafRecord alloc] initWithCKRecord:record];
     [manifestLeaf saveToDatabase:&error];
 }
 
 - (void)_onqueueCKRecordDeviceStateChanged:(CKRecord*)record resync:(bool)resync {
+    dispatch_assert_queue(self.queue);
     if(resync) {
         NSError* dserror = nil;
         CKKSDeviceStateEntry* cdse  = [CKKSDeviceStateEntry tryFromDatabase:record.recordID.recordName zoneID:self.zoneID error:&dserror];
     }
 }
 
+- (bool)_onqueueResetAllInflightOQE:(NSError**)error {
+    dispatch_assert_queue(self.queue);
+    NSError* localError = nil;
+
+    while(true) {
+        NSArray<CKKSOutgoingQueueEntry*> * inflightQueueEntries = [CKKSOutgoingQueueEntry fetch:SecCKKSOutgoingQueueItemsAtOnce
+                                                                                          state:SecCKKSStateInFlight
+                                                                                         zoneID:self.zoneID
+                                                                                          error:&localError];
+
+        if(localError != nil) {
+            ckkserror("ckks", self, "Error finding inflight outgoing queue records: %@", localError);
+            if(error) {
+                *error = localError;
+            }
+            return false;
+        }
+
+        if([inflightQueueEntries count] == 0u) {
+            break;
+        }
+
+        for(CKKSOutgoingQueueEntry* oqe in inflightQueueEntries) {
+            [self _onqueueChangeOutgoingQueueEntry:oqe toState:SecCKKSStateNew error:&localError];
+
+            if(localError) {
+                ckkserror("ckks", self, "Error fixing up inflight OQE(%@): %@", oqe, localError);
+                if(error) {
+                    *error = localError;
+                }
+                return false;
+            }
+        }
+    }
+
+    return true;
+}
+
 - (bool)_onqueueChangeOutgoingQueueEntry: (CKKSOutgoingQueueEntry*) oqe toState: (NSString*) state error: (NSError* __autoreleasing*) error {
     dispatch_assert_queue(self.queue);
 
 
     if([state isEqualToString: SecCKKSStateDeleted]) {
         // Hurray, this must be a success
-        SecBoolNSErrorCallback callback = self.pendingSyncCallbacks[oqe.uuid];
-        if(callback) {
-            callback(true, nil);
+        SecBoolNSErrorCallback syncCallback = [[CKKSViewManager manager] claimCallbackForUUID:oqe.uuid];
+        if(syncCallback) {
+            syncCallback(true, nil);
         }
 
         [oqe deleteFromDatabase: &localerror];
             ckkserror("ckks", self, "Couldn't delete %@: %@", oqe, localerror);
         }
 
+    } else if([oqe.state isEqualToString:SecCKKSStateInFlight] && [state isEqualToString:SecCKKSStateNew]) {
+        // An in-flight OQE is moving to new? See if it's been superceded
+        CKKSOutgoingQueueEntry* newOQE = [CKKSOutgoingQueueEntry tryFromDatabase:oqe.uuid state:SecCKKSStateNew zoneID:self.zoneID error:&localerror];
+        if(localerror) {
+            ckkserror("ckksoutgoing", self, "Couldn't fetch an overwriting OQE, assuming one doesn't exist: %@", localerror);
+            newOQE = nil;
+        }
+
+        if(newOQE) {
+            ckksnotice("ckksoutgoing", self, "New modification has come in behind inflight %@; dropping failed change", oqe);
+            // recurse for that lovely code reuse
+            [self _onqueueChangeOutgoingQueueEntry:oqe toState:SecCKKSStateDeleted error:&localerror];
+            if(localerror) {
+                ckkserror("ckksoutgoing", self, "Couldn't delete in-flight OQE: %@", localerror);
+                if(error) {
+                    *error = localerror;
+                }
+            }
+        } else {
+            oqe.state = state;
+            [oqe saveToDatabase: &localerror];
+            if(localerror) {
+                ckkserror("ckks", self, "Couldn't save %@ as %@: %@", oqe, state, localerror);
+            }
+        }
+
     } else {
         oqe.state = state;
         [oqe saveToDatabase: &localerror];
 - (bool)_onqueueErrorOutgoingQueueEntry: (CKKSOutgoingQueueEntry*) oqe itemError: (NSError*) itemError error: (NSError* __autoreleasing*) error {
     dispatch_assert_queue(self.queue);
 
-    SecBoolNSErrorCallback callback = self.pendingSyncCallbacks[oqe.uuid];
+    SecBoolNSErrorCallback callback = [[CKKSViewManager manager] claimCallbackForUUID:oqe.uuid];
     if(callback) {
         callback(false, itemError);
     }
     NSError* localerror = nil;
 
-    oqe.state = SecCKKSStateError;
-    [oqe saveToDatabase: &localerror];
+    // Now, delete the OQE: it's never coming back
+    [oqe deleteFromDatabase:&localerror];
     if(localerror) {
-        ckkserror("ckks", self, "Couldn't set %@ as error: %@", oqe, localerror);
+        ckkserror("ckks", self, "Couldn't delete %@ (due to error %@): %@", oqe, itemError, localerror);
     }
 
     if(error && localerror) {
     return localerror == nil;
 }
 
-- (bool)_onQueueUpdateLatestManifestWithError:(NSError**)error
+- (bool)dispatchSyncWithConnection:(SecDbConnectionRef _Nonnull)dbconn
+                    readWriteTxion:(BOOL)readWriteTxion
+                             block:(CKKSDatabaseTransactionResult (^)(void))block
 {
-    dispatch_assert_queue(self.queue);
-    CKKSManifest* manifest = [CKKSManifest latestTrustedManifestForZone:self.zoneName error:error];
-    if (manifest) {
-        self.latestManifest = manifest;
-        return true;
-    }
-    else {
-        return false;
+    CFErrorRef cferror = NULL;
+
+    // Take the DB transaction, then get on the local queue.
+    // In the case of exclusive DB transactions, we don't really _need_ the local queue, but, it's here for future use.
+
+    SecDbTransactionType txtionType = readWriteTxion ? kSecDbExclusiveRemoteCKKSTransactionType : kSecDbNormalTransactionType;
+    bool ret = kc_transaction_type(dbconn, txtionType, &cferror, ^bool{
+        __block CKKSDatabaseTransactionResult result = CKKSDatabaseTransactionRollback;
+
+        CKKSSQLInTransaction = true;
+        if(readWriteTxion) {
+            CKKSSQLInWriteTransaction = true;
+        }
+
+        dispatch_sync(self.queue, ^{
+            result = block();
+        });
+
+        if(readWriteTxion) {
+            CKKSSQLInWriteTransaction = false;
+        }
+        CKKSSQLInTransaction = false;
+        return result == CKKSDatabaseTransactionCommit;
+    });
+
+    if(cferror) {
+        ckkserror("ckks", self, "error doing database transaction, major problems ahead: %@", cferror);
     }
+    return ret;
 }
 
-- (bool)checkTLK: (CKKSKey*) proposedTLK error: (NSError * __autoreleasing *) error {
-    // Until we have Octagon Trust, accept this TLK iff we have its actual AES key in the keychain
+- (void)dispatchSyncWithSQLTransaction:(CKKSDatabaseTransactionResult (^)(void))block
+{
+    // important enough to block this thread. Must get a connection first, though!
+
+    // Please don't jetsam us...
+    os_transaction_t transaction = os_transaction_create([[NSString stringWithFormat:@"com.apple.securityd.ckks.%@", self.zoneName] UTF8String]);
 
-    if([proposedTLK loadKeyMaterialFromKeychain:error]) {
-        // Hurray!
-        return true;
-    } else {
-        return false;
+    CFErrorRef cferror = NULL;
+    kc_with_dbt(true, &cferror, ^bool (SecDbConnectionRef dbt) {
+        return [self dispatchSyncWithConnection:dbt
+                                 readWriteTxion:YES
+                                          block:block];
+
+    });
+    if(cferror) {
+        ckkserror("ckks", self, "error getting database connection, major problems ahead: %@", cferror);
     }
+
+    (void)transaction;
 }
 
-- (void) dispatchAsync: (bool (^)(void)) block {
-    // We need to call kc_with_dbt, which blocks. Route up through a global queue...
-    __weak __typeof(self) weakSelf = self;
+- (void)dispatchSyncWithReadOnlySQLTransaction:(void (^)(void))block
+{
+    // Please don't jetsam us...
+    os_transaction_t transaction = os_transaction_create([[NSString stringWithFormat:@"com.apple.securityd.ckks.%@", self.zoneName] UTF8String]);
+
+    CFErrorRef cferror = NULL;
+
+    // Note: we are lying to kc_with_dbt here about whether we're read-and-write or read-only.
+    // This is because the SOS engine's queue are broken: SOSEngineSetNotifyPhaseBlock attempts
+    // to take the SOS engine's queue while a SecDb transaction is still ongoing. But, in
+    // SOSEngineCopyPeerConfirmedDigests, SOS takes the engine queue, then calls dsCopyManifestWithViewNameSet()
+    // which attempts to get a read-only SecDb connection.
+    //
+    // The issue manifests when many CKKS read-only transactions are in-flight, and starve out
+    // the pool of read-only connections. Then, a deadlock forms.
+    //
+    // By claiming to be a read-write connection here, we'll contend on the pool of writer threads,
+    // and shouldn't starve SOS of its read thread.
+    //
+    // But, since we pass NO to readWriteTxion, the SQLite transaction will be of type
+    // kSecDbNormalTransactionType, which won't block other readers.
+
+    kc_with_dbt(true, &cferror, ^bool (SecDbConnectionRef dbt) {
+        return [self dispatchSyncWithConnection:dbt
+                                 readWriteTxion:NO
+                                          block:^CKKSDatabaseTransactionResult {
+            block();
+            return CKKSDatabaseTransactionCommit;
+        }];
 
-    dispatch_async(dispatch_get_global_queue(QOS_CLASS_DEFAULT, 0), ^{
-        [weakSelf dispatchSync:block];
     });
+    if(cferror) {
+        ckkserror("ckks", self, "error getting database connection, major problems ahead: %@", cferror);
+    }
+
+    (void)transaction;
 }
 
-// Use this if you have a potential database connection already
-- (void) dispatchSyncWithConnection: (SecDbConnectionRef) dbconn block: (bool (^)(void)) block {
-    if(dbconn) {
-        dispatch_sync(self.queue, ^{
-            CFErrorRef cferror = NULL;
-            kc_transaction_type(dbconn, kSecDbExclusiveRemoteCKKSTransactionType, &cferror, block);
+- (BOOL)insideSQLTransaction
+{
+    return CKKSSQLInTransaction;
+}
 
-            if(cferror) {
-                ckkserror("ckks", self, "error doing database transaction (sync), major problems ahead: %@", cferror);
-            }
-        });
+#pragma mark - CKKSZone operations
+
+- (void)beginCloudKitOperation
+{
+    [self.accountTracker registerForNotificationsOfCloudKitAccountStatusChange:self];
+}
+
+- (CKKSResultOperation*)createAccountLoggedInDependency:(NSString*)message
+{
+    WEAKIFY(self);
+    CKKSResultOperation* accountLoggedInDependency = [CKKSResultOperation named:@"account-logged-in-dependency" withBlock:^{
+        STRONGIFY(self);
+        ckksnotice("ckkszone", self, "%@", message);
+    }];
+    accountLoggedInDependency.descriptionErrorCode = CKKSResultDescriptionPendingAccountLoggedIn;
+    return accountLoggedInDependency;
+}
+
+#pragma mark - CKKSZoneUpdateReceiverProtocol
+
+- (CKKSAccountStatus)accountStatusFromCKAccountInfo:(CKAccountInfo*)info
+{
+    if(!info) {
+        return CKKSAccountStatusUnknown;
+    }
+    if(info.accountStatus == CKAccountStatusAvailable &&
+       info.hasValidCredentials) {
+        return CKKSAccountStatusAvailable;
     } else {
-        [self dispatchSync: block];
+        return CKKSAccountStatusNoAccount;
     }
 }
 
-- (void) dispatchSync: (bool (^)(void)) block {
-    // important enough to block this thread. Must get a connection first, though!
-    __weak __typeof(self) weakSelf = self;
+- (void)cloudkitAccountStateChange:(CKAccountInfo* _Nullable)oldAccountInfo to:(CKAccountInfo*)currentAccountInfo
+{
+    ckksnotice("ckkszone", self, "%@ Received notification of CloudKit account status change, moving from %@ to %@",
+               self.zoneID.zoneName,
+               oldAccountInfo,
+               currentAccountInfo);
 
-    CFErrorRef cferror = NULL;
-    kc_with_dbt(true, &cferror, ^bool (SecDbConnectionRef dbt) {
-        __strong __typeof(weakSelf) strongSelf = weakSelf;
-        if(!strongSelf) {
-            ckkserror("ckks", strongSelf, "received callback for released object");
-            return false;
+    // Filter for device2device encryption and cloudkit grey mode
+    CKKSAccountStatus oldStatus = [self accountStatusFromCKAccountInfo:oldAccountInfo];
+    CKKSAccountStatus currentStatus = [self accountStatusFromCKAccountInfo:currentAccountInfo];
+
+    if(oldStatus == currentStatus) {
+        ckksnotice("ckkszone", self, "Computed status of new CK account info is same as old status: %@", [CKKSAccountStateTracker stringFromAccountStatus:currentStatus]);
+        return;
+    }
+
+    switch(currentStatus) {
+        case CKKSAccountStatusAvailable: {
+            ckksnotice("ckkszone", self, "Logged into iCloud.");
+            [self handleCKLogin];
+
+            if(self.accountLoggedInDependency) {
+                [self.operationQueue addOperation:self.accountLoggedInDependency];
+                self.accountLoggedInDependency = nil;
+            };
         }
+            break;
 
-        __block bool ok = false;
-        __block CFErrorRef cferror = NULL;
+        case CKKSAccountStatusNoAccount: {
+            ckksnotice("ckkszone", self, "Logging out of iCloud. Shutting down.");
 
-        dispatch_sync(strongSelf.queue, ^{
-            ok = kc_transaction_type(dbt, kSecDbExclusiveRemoteCKKSTransactionType, &cferror, block);
-        });
-        return ok;
+            if(!self.accountLoggedInDependency) {
+                self.accountLoggedInDependency = [self createAccountLoggedInDependency:@"CloudKit account logged in again."];
+            }
+
+            [self handleCKLogout];
+        }
+            break;
+
+        case CKKSAccountStatusUnknown: {
+            // We really don't expect to receive this as a notification, but, okay!
+            ckksnotice("ckkszone", self, "Account status has become undetermined. Pausing for %@", self.zoneID.zoneName);
+
+            if(!self.accountLoggedInDependency) {
+                self.accountLoggedInDependency = [self createAccountLoggedInDependency:@"CloudKit account logged in again."];
+            }
+
+            [self handleCKLogout];
+        }
+            break;
+    }
+}
+
+- (void)handleCKLogin
+{
+    ckksnotice("ckks", self, "received a notification of CK login");
+    if(!SecCKKSIsEnabled()) {
+        ckksnotice("ckks", self, "Skipping CloudKit initialization due to disabled CKKS");
+        return;
+    }
+
+    dispatch_sync(self.queue, ^{
+        ckksinfo("ckkszone", self, "received a notification of CK login");
+
+        // Change our condition variables to reflect that we think we're logged in
+        self.accountStatus = CKKSAccountStatusAvailable;
+        self.loggedOut = [[CKKSCondition alloc] initToChain:self.loggedOut];
+        [self.loggedIn fulfill];
     });
-    if(cferror) {
-        ckkserror("ckks", self, "error getting database connection (sync), major problems ahead: %@", cferror);
+
+    [self.stateMachine handleFlag:CKKSFlagCloudKitLoggedIn];
+
+    [self.accountStateKnown fulfill];
+}
+
+- (void)handleCKLogout
+{
+    dispatch_sync(self.queue, ^{
+        ckksinfo("ckkszone", self, "received a notification of CK logout");
+
+        self.accountStatus = CKKSAccountStatusNoAccount;
+        self.loggedIn = [[CKKSCondition alloc] initToChain:self.loggedIn];
+        [self.loggedOut fulfill];
+    });
+
+    [self.stateMachine handleFlag:CKKSFlagCloudKitLoggedOut];
+
+    [self.accountStateKnown fulfill];
+}
+
+#pragma mark - Trust operations
+
+- (void)beginTrustedOperation:(NSArray<id<CKKSPeerProvider>>*)peerProviders
+             suggestTLKUpload:(CKKSNearFutureScheduler*)suggestTLKUpload
+           requestPolicyCheck:(CKKSNearFutureScheduler*)requestPolicyCheck
+{
+    for(id<CKKSPeerProvider> peerProvider in peerProviders) {
+        [peerProvider registerForPeerChangeUpdates:self];
     }
+
+    [self.launch addEvent:@"beginTrusted"];
+
+    dispatch_sync(self.queue, ^{
+        ckksnotice("ckkstrust", self, "Beginning trusted operation");
+        self.operationDependencies.peerProviders = peerProviders;
+
+        CKKSAccountStatus oldTrustStatus = self.trustStatus;
+
+        self.suggestTLKUpload = suggestTLKUpload;
+        self.requestPolicyCheck = requestPolicyCheck;
+
+        self.trustStatus = CKKSAccountStatusAvailable;
+        [self.stateMachine _onqueueHandleFlag:CKKSFlagBeginTrustedOperation];
+
+        if(oldTrustStatus == CKKSAccountStatusNoAccount) {
+            ckksnotice("ckkstrust", self, "Moving from an untrusted status; we need to process incoming queue and scan for any new items");
+
+            [self.stateMachine _onqueueHandleFlag:CKKSFlagProcessIncomingQueue];
+            [self.stateMachine _onqueueHandleFlag:CKKSFlagScanLocalItems];
+        }
+    });
 }
 
-- (void)dispatchSyncWithAccountQueue:(bool (^)(void))block
+- (void)endTrustedOperation
 {
-    [SOSAccount performOnAccountQueue:^{
-        [CKKSManifest performWithAccountInfo:^{
-            [self dispatchSync:^bool{
-                __block bool result = false;
-                [SOSAccount performWhileHoldingAccountQueue:^{ // so any calls through SOS account will know they can perform their work without dispatching to the account queue, which we already hold
-                    result = block();
-                }];
-                return result;
-            }];
-        }];
-    }];
+    [self.launch addEvent:@"endTrusted"];
+
+    dispatch_sync(self.queue, ^{
+        ckksnotice("ckkstrust", self, "Ending trusted operation");
+
+        self.operationDependencies.peerProviders = @[];
+
+        self.suggestTLKUpload = nil;
+
+        self.trustStatus = CKKSAccountStatusNoAccount;
+        [self.stateMachine _onqueueHandleFlag:CKKSFlagEndTrustedOperation];
+    });
+}
+
+- (BOOL)itemSyncingEnabled
+{
+    if(!self.operationDependencies.syncingPolicy) {
+        ckksnotice("ckks", self, "No syncing policy loaded; item syncing is disabled");
+        return NO;
+    } else {
+        return [self.operationDependencies.syncingPolicy isSyncingEnabledForView:self.zoneName];
+    }
+}
+
+- (void)setCurrentSyncingPolicy:(TPSyncingPolicy*)syncingPolicy policyIsFresh:(BOOL)policyIsFresh
+{
+    dispatch_sync(self.queue, ^{
+        BOOL oldEnabled = [self itemSyncingEnabled];
+
+        self.operationDependencies.syncingPolicy = syncingPolicy;
+
+        BOOL enabled = [self itemSyncingEnabled];
+        if(enabled != oldEnabled) {
+            ckksnotice("ckks", self, "Syncing for this view is now %@ (policy: %@)", enabled ? @"enabled" : @"paused", self.operationDependencies.syncingPolicy);
+        }
+
+        if(enabled) {
+            CKKSResultOperation* incomingOp = [self processIncomingQueue:false after:nil policyConsideredAuthoritative:policyIsFresh];
+            [self processOutgoingQueueAfter:incomingOp ckoperationGroup:nil];
+        }
+    });
 }
 
-#pragma mark - CKKSZoneUpdateReceiver
+- (void)receivedItemForWrongView
+{
+    [self.requestPolicyCheck trigger];
+}
 
-- (void)notifyZoneChange: (CKRecordZoneNotification*) notification {
-    ckksinfo("ckks", self, "hurray, got a zone change for %@ %@", self, notification);
+#pragma mark - CKKSChangeFetcherClient
 
-    [self fetchAndProcessCKChanges:CKKSFetchBecauseAPNS];
+- (BOOL)zoneIsReadyForFetching
+{
+    __block BOOL ready = NO;
+
+    [self dispatchSyncWithReadOnlySQLTransaction:^{
+        ready = (bool)[self _onQueueZoneIsReadyForFetching];
+    }];
+
+    return ready;
 }
 
-// Must be on the queue when this is called
-- (void)handleCKLogin {
+- (BOOL)_onQueueZoneIsReadyForFetching
+{
     dispatch_assert_queue(self.queue);
+    if(self.accountStatus != CKKSAccountStatusAvailable) {
+        ckksnotice("ckksfetch", self, "Not participating in fetch: not logged in");
+        return NO;
+    }
 
-    if(!self.setupStarted) {
-        [self _onqueueInitializeZone];
-    } else {
-        ckksinfo("ckks", self, "ignoring login as setup has already started");
+    CKKSZoneStateEntry* ckse = [CKKSZoneStateEntry state:self.operationDependencies.zoneID.zoneName];
+
+    if(!ckse.ckzonecreated) {
+        ckksnotice("ckksfetch", self, "Not participating in fetch: zone not created yet");
+        return NO;
     }
+    return YES;
 }
 
-- (void)handleCKLogout {
-    NSBlockOperation* logout = [NSBlockOperation blockOperationWithBlock: ^{
-        [self dispatchSync:^bool {
-            ckksnotice("ckks", self, "received a notification of CK logout for %@", self.zoneName);
-            NSError* error = nil;
+- (CKKSCloudKitFetchRequest*)participateInFetch
+{
+    __block CKKSCloudKitFetchRequest* request = [[CKKSCloudKitFetchRequest alloc] init];
 
-            [self _onqueueResetLocalData: &error];
+    [self dispatchSyncWithReadOnlySQLTransaction:^{
+        if (![self _onQueueZoneIsReadyForFetching]) {
+            ckksnotice("ckksfetch", self, "skipping fetch since zones are not ready");
+            return;
+        }
 
-            if(error) {
-                ckkserror("ckks", self, "error while resetting local data: %@", error);
+        request.participateInFetch = true;
+        [self.launch addEvent:@"fetch"];
+
+        if([self.keyHierarchyState isEqualToString:SecCKKSZoneKeyStateNeedFullRefetch]) {
+            // We want to return a nil change tag (to force a resync)
+            ckksnotice("ckksfetch", self, "Beginning refetch");
+            request.changeToken = nil;
+            request.resync = true;
+        } else {
+            CKKSZoneStateEntry* ckse = [CKKSZoneStateEntry state:self.zoneName];
+            if(!ckse) {
+                ckkserror("ckksfetch", self, "couldn't fetch zone change token for %@", self.zoneName);
+                return;
             }
-            return true;
-        }];
+            request.changeToken = ckse.changeToken;
+        }
     }];
 
-    logout.name = @"cloudkit-logout";
-    [self scheduleAccountStatusOperation: logout];
+    if (request.changeToken == nil) {
+        self.launch.firstLaunch = true;
+    }
+
+    return request;
 }
 
-#pragma mark - CKKSChangeFetcherErrorOracle
+- (void)changesFetched:(NSArray<CKRecord*>*)changedRecords
+      deletedRecordIDs:(NSArray<CKKSCloudKitDeletion*>*)deletedRecords
+        newChangeToken:(CKServerChangeToken*)newChangeToken
+            moreComing:(BOOL)moreComing
+                resync:(BOOL)resync
+{
+    [self.launch addEvent:@"changes-fetched"];
 
-- (bool) isFatalCKFetchError: (NSError*) error {
-    __weak __typeof(self) weakSelf = self;
+    if(changedRecords.count == 0 && deletedRecords.count == 0 && !moreComing && !resync) {
+        // Early-exit, so we don't pick up the account keys or kick off an IncomingQueue operation for no changes
+        [self dispatchSyncWithSQLTransaction:^CKKSDatabaseTransactionResult{
+            ckksinfo("ckksfetch", self, "No record changes in this fetch");
 
-    // Again, note that this handles exactly one zone. Mutli-zone errors are not supported.
-    bool isChangeTokenExpiredError = false;
-    if([error.domain isEqualToString:CKErrorDomain] && (error.code == CKErrorChangeTokenExpired)) {
-        isChangeTokenExpiredError = true;
-    } else if([error.domain isEqualToString:CKErrorDomain] && (error.code == CKErrorPartialFailure)) {
-        NSDictionary* partialErrors = error.userInfo[CKPartialErrorsByItemIDKey];
-        for(NSError* partialError in partialErrors.allValues) {
-            if([partialError.domain isEqualToString:CKErrorDomain] && (partialError.code == CKErrorChangeTokenExpired)) {
-                isChangeTokenExpiredError = true;
+            NSError* error = nil;
+            CKKSZoneStateEntry* state = [CKKSZoneStateEntry state:self.zoneName];
+            state.lastFetchTime = [NSDate date]; // The last fetch happened right now!
+            state.changeToken = newChangeToken;
+            state.moreRecordsInCloudKit = moreComing;
+            [state saveToDatabase:&error];
+            if(error) {
+                ckkserror("ckksfetch", self, "Couldn't save new server change token: %@", error);
             }
-        }
+            return CKKSDatabaseTransactionCommit;
+        }];
+        return;
     }
 
-    if(isChangeTokenExpiredError) {
-        ckkserror("ckks", self, "Received notice that our change token is out of date. Resetting local data...");
-        [self cancelAllOperations];
-        CKKSResultOperation* resetOp = [self resetLocalData];
-        CKKSResultOperation* resetHandler = [CKKSResultOperation named:@"local-reset-handler" withBlock:^{
-            __strong __typeof(self) strongSelf = weakSelf;
-            if(!strongSelf) {
-                ckkserror("ckks", strongSelf, "received callback for released object");
-                return;
+    [self dispatchSyncWithSQLTransaction:^CKKSDatabaseTransactionResult{
+        for (CKRecord* record in changedRecords) {
+            [self _onqueueCKRecordChanged:record resync:resync];
+        }
+
+        for (CKKSCloudKitDeletion* deletion in deletedRecords) {
+            [self _onqueueCKRecordDeleted:deletion.recordID recordType:deletion.recordType resync:resync];
+        }
+
+        NSError* error = nil;
+        if(resync) {
+            // If we're performing a resync, we need to keep track of everything that's actively in
+            // CloudKit during the fetch, (so that we can find anything that's on-disk and not in CloudKit).
+            // Please note that if, during a resync, the fetch errors, we won't be notified. If a record is in
+            // the first refetch but not the second, it'll be added to our set, and the second resync will not
+            // delete the record (which is a consistency violation, but only with actively changing records).
+            // A third resync should correctly delete that record.
+
+            if(self.resyncRecordsSeen == nil) {
+                self.resyncRecordsSeen = [NSMutableSet set];
+            }
+            for(CKRecord* r in changedRecords) {
+                [self.resyncRecordsSeen addObject:r.recordID.recordName];
             }
 
-            if(resetOp.error) {
-                ckksnotice("ckks", strongSelf, "CloudKit-inspired local reset of %@ ended with error: %@", strongSelf.zoneID, error);
+            // Is there More Coming? If not, self.resyncRecordsSeen contains everything in CloudKit. Inspect for anything extra!
+            if(moreComing) {
+                ckksnotice("ckksresync", self, "In a resync, but there's More Coming. Waiting to scan for extra items.");
+
             } else {
-                ckksnotice("ckksreset", strongSelf, "re-initializing zone %@", strongSelf.zoneID);
-                [strongSelf initializeZone];
+                // Scan through all CKMirrorEntries and determine if any exist that CloudKit didn't tell us about
+                ckksnotice("ckksresync", self, "Comparing local UUIDs against the CloudKit list");
+                NSMutableArray<NSString*>* uuids = [[CKKSMirrorEntry allUUIDs:self.zoneID error:&error] mutableCopy];
+
+                for(NSString* uuid in uuids) {
+                    if([self.resyncRecordsSeen containsObject:uuid]) {
+                        ckksnotice("ckksresync", self, "UUID %@ is still in CloudKit; carry on.", uuid);
+                    } else {
+                        CKKSMirrorEntry* ckme = [CKKSMirrorEntry tryFromDatabase:uuid zoneID:self.zoneID error:&error];
+                        if(error != nil) {
+                            ckkserror("ckksresync", self, "Couldn't read an item from the database, but it used to be there: %@ %@", uuid, error);
+                            continue;
+                        }
+                        if(!ckme) {
+                            ckkserror("ckksresync", self, "Couldn't read ckme(%@) from database; continuing", uuid);
+                            continue;
+                        }
+
+                        ckkserror("ckksresync", self, "BUG: Local item %@ not found in CloudKit, deleting", uuid);
+                        [self _onqueueCKRecordDeleted:ckme.item.storedCKRecord.recordID recordType:ckme.item.storedCKRecord.recordType resync:resync];
+                    }
+                }
+
+                // Now that we've inspected resyncRecordsSeen, reset it for the next time through
+                self.resyncRecordsSeen = nil;
             }
-        }];
+        }
 
-        [resetHandler addDependency:resetOp];
-        [self scheduleOperation:resetHandler];
-        return true;
-    }
+        CKKSZoneStateEntry* state = [CKKSZoneStateEntry state:self.zoneName];
+        state.lastFetchTime = [NSDate date]; // The last fetch happened right now!
+        state.changeToken = newChangeToken;
+        state.moreRecordsInCloudKit = moreComing;
+        [state saveToDatabase:&error];
+        if(error) {
+            ckkserror("ckksfetch", self, "Couldn't save new server change token: %@", error);
+        }
+
+        if(!moreComing) {
+            // Might as well kick off a IQO!
+            [self processIncomingQueue:false];
+            ckksnotice("ckksfetch", self, "Beginning incoming processing for %@", self.zoneID);
+        }
+
+        ckksnotice("ckksfetch", self, "Finished processing changes for %@", self.zoneID);
 
-    bool isDeletedZoneError = false;
-    if([error.domain isEqualToString:CKErrorDomain] && ((error.code == CKErrorUserDeletedZone) || (error.code == CKErrorZoneNotFound))) {
-        isDeletedZoneError = true;
-    } else if([error.domain isEqualToString:CKErrorDomain] && (error.code == CKErrorPartialFailure)) {
+        return CKKSDatabaseTransactionCommit;
+    }];
+}
+
+- (bool)ckErrorOrPartialError:(NSError *)error isError:(CKErrorCode)errorCode
+{
+    if((error.code == errorCode) && [error.domain isEqualToString:CKErrorDomain]) {
+        return true;
+    } else if((error.code == CKErrorPartialFailure) && [error.domain isEqualToString:CKErrorDomain]) {
         NSDictionary* partialErrors = error.userInfo[CKPartialErrorsByItemIDKey];
-        for(NSError* partialError in partialErrors.allValues) {
-            if([partialError.domain isEqualToString:CKErrorDomain] && ((partialError.code == CKErrorUserDeletedZone) || (partialError.code == CKErrorZoneNotFound))) {
-                isDeletedZoneError = true;
-            }
+
+        NSError* partialError = partialErrors[self.zoneID];
+        if ((partialError.code == errorCode) && [partialError.domain isEqualToString:CKErrorDomain]) {
+            return true;
         }
     }
+    return false;
+}
 
-    if(isDeletedZoneError) {
-        ckkserror("ckks", self, "Received notice that our zone does not exist. Resetting all data.");
-        [self cancelAllOperations];
-        CKKSResultOperation* resetOp = [self resetCloudKitZone];
-        CKKSResultOperation* resetHandler = [CKKSResultOperation named:@"reset-handler" withBlock:^{
-            __strong __typeof(self) strongSelf = weakSelf;
-            if(!strongSelf) {
-                ckkserror("ckks", strongSelf, "received callback for released object");
-                return;
-            }
+- (bool)shouldRetryAfterFetchError:(NSError*)error {
 
-            if(resetOp.error) {
-                ckksnotice("ckks", strongSelf, "CloudKit-inspired zone reset of %@ ended with error: %@", strongSelf.zoneID, resetOp.error);
-            } else {
-                ckksnotice("ckksreset", strongSelf, "re-initializing zone %@", strongSelf.zoneID);
-                [strongSelf initializeZone];
-            }
-        }];
+    bool isChangeTokenExpiredError = [self ckErrorOrPartialError:error isError:CKErrorChangeTokenExpired];
+    if(isChangeTokenExpiredError) {
+        ckkserror("ckks", self, "Received notice that our change token is out of date (for %@). Resetting local data...", self.zoneID);
 
-        [resetHandler addDependency:resetOp];
-        [self scheduleOperation:resetHandler];
+        [self.stateMachine handleFlag:CKKSFlagChangeTokenExpired];
         return true;
     }
 
+    bool isDeletedZoneError = [self ckErrorOrPartialError:error isError:CKErrorZoneNotFound];
+    if(isDeletedZoneError) {
+        ckkserror("ckks", self, "Received notice that our zone(%@) does not exist. Resetting local data.", self.zoneID);
+
+        [self.stateMachine handleFlag:CKKSFlagCloudKitZoneMissing];
+        return false;
+    }
+
     if([error.domain isEqualToString:CKErrorDomain] && (error.code == CKErrorBadContainer)) {
         ckkserror("ckks", self, "Received notice that our container does not exist. Nothing to do.");
-        return true;
+        return false;
     }
 
-    return false;
+    return true;
+}
+
+#pragma mark CKKSPeerUpdateListener
+
+- (void)selfPeerChanged:(id<CKKSPeerProvider>)provider
+{
+    // Currently, we have no idea what to do with this. Kick off a key reprocess?
+    ckkserror("ckks", self, "Received update that our self identity has changed");
+    [self keyStateMachineRequestProcess];
+}
+
+- (void)trustedPeerSetChanged:(id<CKKSPeerProvider>)provider
+{
+    // We might need to share the TLK to some new people, or we might now trust the TLKs we have.
+    // The key state machine should handle that, so poke it.
+    ckkserror("ckks", self, "Received update that the trust set has changed");
+
+    [self.stateMachine handleFlag:CKKSFlagTrustedPeersSetChanged];
 }
 
 #pragma mark - Test Support
 
 - (bool) outgoingQueueEmpty: (NSError * __autoreleasing *) error {
     __block bool ret = false;
-    [self dispatchSync: ^bool{
+    [self dispatchSyncWithReadOnlySQLTransaction:^{
         NSArray* queueEntries = [CKKSOutgoingQueueEntry all: error];
         ret = queueEntries && ([queueEntries count] == 0);
-        return true;
     }];
 
     return ret;
 }
 
-- (CKKSResultOperation*)waitForFetchAndIncomingQueueProcessing {
-    if(!SecCKKSIsEnabled()) {
-        ckksinfo("ckks", self, "Due to disabled CKKS, returning fast from waitForFetchAndIncomingQueueProcessing");
-        return nil;
-    }
-
-    CKKSResultOperation* op = [self fetchAndProcessCKChanges:CKKSFetchBecauseTesting];
-    [op waitUntilFinished];
-    return op;
+- (void)waitForFetchAndIncomingQueueProcessing
+{
+    [[self.zoneChangeFetcher inflightFetch] waitUntilFinished];
+    [self waitForOperationsOfClass:[CKKSIncomingQueueOperation class]];
 }
 
 - (void)waitForKeyHierarchyReadiness {
     }
 }
 
+#pragma mark - NSOperation assistance
+
+- (void)scheduleOperation:(NSOperation*)op
+{
+    if(self.halted) {
+        ckkserror("ckkszone", self, "attempted to schedule an operation on a halted zone, ignoring");
+        return;
+    }
+
+    [op addNullableDependency:self.accountLoggedInDependency];
+    [self.operationQueue addOperation: op];
+}
+
+// to be used rarely, if at all
+- (bool)scheduleOperationWithoutDependencies:(NSOperation*)op
+{
+    if(self.halted) {
+        ckkserror("ckkszone", self, "attempted to schedule an non-dependent operation on a halted zone, ignoring");
+        return false;
+    }
+
+    [self.operationQueue addOperation: op];
+    return true;
+}
+
+- (void)waitUntilAllOperationsAreFinished
+{
+    [self.operationQueue waitUntilAllOperationsAreFinished];
+}
+
+- (void)waitForOperationsOfClass:(Class)operationClass
+{
+    NSArray* operations = [self.operationQueue.operations copy];
+    for(NSOperation* op in operations) {
+        if([op isKindOfClass:operationClass]) {
+            [op waitUntilFinished];
+        }
+    }
+}
+
+- (void)cancelPendingOperations {
+    @synchronized(self.outgoingQueueOperations) {
+        for(NSOperation* op in self.outgoingQueueOperations) {
+            [op cancel];
+        }
+        [self.outgoingQueueOperations removeAllObjects];
+    }
+
+    @synchronized(self.incomingQueueOperations) {
+        for(NSOperation* op in self.incomingQueueOperations) {
+            [op cancel];
+        }
+        [self.incomingQueueOperations removeAllObjects];
+    }
+
+    @synchronized(self.scanLocalItemsOperations) {
+        for(NSOperation* op in self.scanLocalItemsOperations) {
+            [op cancel];
+        }
+        [self.scanLocalItemsOperations removeAllObjects];
+    }
+}
+
 - (void)cancelAllOperations {
-    [self.zoneSetupOperation cancel];
-    [self.keyStateMachineOperation cancel];
     [self.keyStateReadyDependency cancel];
     [self.zoneChangeFetcher cancel];
+    [self.notifyViewChangedScheduler cancel];
 
-    [super cancelAllOperations];
+    [self cancelPendingOperations];
+    [self.operationQueue cancelAllOperations];
+}
 
-    [self dispatchSync:^bool{
-        [self _onqueueAdvanceKeyStateMachineToState: SecCKKSZoneKeyStateCancelled withError: nil];
-        return true;
-    }];
+- (void)halt {
+    [self.stateMachine haltOperation];
+
+    // Synchronously set the 'halted' bit
+    dispatch_sync(self.queue, ^{
+        self.halted = true;
+    });
+
+    // Bring all operations down, too
+    [self cancelAllOperations];
+
+    // And now, wait for all operations that are running
+    for(NSOperation* op in self.operationQueue.operations) {
+        if(op.isExecuting) {
+            [op waitUntilFinished];
+        }
+    }
+
+    // Don't send any more notifications, either
+    _notifierClass = nil;
 }
 
 - (NSDictionary*)status {
 #define stringify(obj) CKKSNilToNSNull([obj description])
 #define boolstr(obj) (!!(obj) ? @"yes" : @"no")
-    __block NSDictionary* ret = nil;
+    __block NSMutableDictionary* ret = nil;
     __block NSError* error = nil;
-    CKKSManifest* manifest = [CKKSManifest latestTrustedManifestForZone:self.zoneName error:&error];
-    [self dispatchSync: ^bool {
 
-        NSString* uuidTLK    = [CKKSKey currentKeyForClass:SecCKKSKeyClassTLK zoneID:self.zoneID error:&error].uuid;
-        NSString* uuidClassA = [CKKSKey currentKeyForClass:SecCKKSKeyClassA   zoneID:self.zoneID error:&error].uuid;
-        NSString* uuidClassC = [CKKSKey currentKeyForClass:SecCKKSKeyClassC   zoneID:self.zoneID error:&error].uuid;
-        
-        NSString* manifestGeneration = manifest ? [NSString stringWithFormat:@"%lu", (unsigned long)manifest.generationCount] : nil;
+    ret = [[self fastStatus] mutableCopy];
+
+    [self dispatchSyncWithReadOnlySQLTransaction:^{
+        CKKSCurrentKeySet* keyset = [CKKSCurrentKeySet loadForZone:self.zoneID];
+        if(keyset.error) {
+            error = keyset.error;
+        }
 
         if(error) {
             ckkserror("ckks", self, "error during status: %@", error);
             [mutDeviceStates addObject: [obj description]];
         }];
 
-        ret = @{
-                 @"view":                CKKSNilToNSNull(self.zoneName),
-                 @"ckaccountstatus":     self.accountStatus == CKAccountStatusCouldNotDetermine ? @"could not determine" :
-                                         self.accountStatus == CKAccountStatusAvailable         ? @"logged in" :
-                                         self.accountStatus == CKAccountStatusRestricted        ? @"restricted" :
-                                         self.accountStatus == CKAccountStatusNoAccount         ? @"logged out" : @"unknown",
-                 @"lockstatetracker":    stringify(self.lockStateTracker),
-                 @"accounttracker":      stringify(self.accountTracker),
-                 @"fetcher":             stringify(self.zoneChangeFetcher),
-                 @"setup":               boolstr(self.setupComplete),
-                 @"zoneCreated":         boolstr(self.zoneCreated),
-                 @"zoneCreatedError":    stringify(self.zoneCreatedError),
-                 @"zoneSubscribed":      boolstr(self.zoneSubscribed),
-                 @"zoneSubscribedError": stringify(self.zoneSubscribedError),
-                 @"zoneInitializeScheduler": stringify(self.initializeScheduler),
-                 @"keystate":            CKKSNilToNSNull(self.keyHierarchyState),
-                 @"keyStateError":       stringify(self.keyHierarchyError),
+        NSArray* tlkShares = [CKKSTLKShareRecord allForUUID:keyset.currentTLKPointer.currentKeyUUID zoneID:self.zoneID error:&error];
+        NSMutableArray<NSString*>* mutTLKShares = [[NSMutableArray alloc] init];
+        [tlkShares enumerateObjectsUsingBlock:^(id  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
+            [mutTLKShares addObject: [obj description]];
+        }];
+
+        [ret addEntriesFromDictionary:@{
                  @"statusError":         stringify(error),
-                 @"oqe":                 CKKSNilToNSNull([CKKSOutgoingQueueEntry countsByState:self.zoneID error:&error]),
-                 @"iqe":                 CKKSNilToNSNull([CKKSIncomingQueueEntry countsByState:self.zoneID error:&error]),
+                 @"oqe":                 CKKSNilToNSNull([CKKSOutgoingQueueEntry countsByStateInZone:self.zoneID error:&error]),
+                 @"iqe":                 CKKSNilToNSNull([CKKSIncomingQueueEntry countsByStateInZone:self.zoneID error:&error]),
                  @"ckmirror":            CKKSNilToNSNull([CKKSMirrorEntry        countsByParentKey:self.zoneID error:&error]),
                  @"devicestates":        CKKSNilToNSNull(mutDeviceStates),
+                 @"tlkshares":           CKKSNilToNSNull(mutTLKShares),
                  @"keys":                CKKSNilToNSNull([CKKSKey countsByClass:self.zoneID error:&error]),
-                 @"currentTLK":          CKKSNilToNSNull(uuidTLK),
-                 @"currentClassA":       CKKSNilToNSNull(uuidClassA),
-                 @"currentClassC":       CKKSNilToNSNull(uuidClassC),
-                 @"currentManifestGen":   CKKSNilToNSNull(manifestGeneration),
-
-                 @"zoneSetupOperation":                 stringify(self.zoneSetupOperation),
-                 @"viewSetupOperation":                 stringify(self.viewSetupOperation),
-                 @"keyStateOperation":                  stringify(self.keyStateMachineOperation),
-                 @"lastIncomingQueueOperation":         stringify(self.lastIncomingQueueOperation),
-                 @"lastNewTLKOperation":                stringify(self.lastNewTLKOperation),
-                 @"lastOutgoingQueueOperation":         stringify(self.lastOutgoingQueueOperation),
-                 @"lastRecordZoneChangesOperation":     stringify(self.lastRecordZoneChangesOperation),
-                 @"lastProcessReceivedKeysOperation":   stringify(self.lastProcessReceivedKeysOperation),
-                 @"lastReencryptOutgoingItemsOperation":stringify(self.lastReencryptOutgoingItemsOperation),
-                 @"lastScanLocalItemsOperation":        stringify(self.lastScanLocalItemsOperation),
-                 };
-        return false;
+                 @"currentTLK":          CKKSNilToNSNull(keyset.tlk.uuid),
+                 @"currentClassA":       CKKSNilToNSNull(keyset.classA.uuid),
+                 @"currentClassC":       CKKSNilToNSNull(keyset.classC.uuid),
+                 @"currentTLKPtr":       CKKSNilToNSNull(keyset.currentTLKPointer.currentKeyUUID),
+                 @"currentClassAPtr":    CKKSNilToNSNull(keyset.currentClassAPointer.currentKeyUUID),
+                 @"currentClassCPtr":    CKKSNilToNSNull(keyset.currentClassCPointer.currentKeyUUID),
+                 @"itemsyncing":         self.itemSyncingEnabled ? @"enabled" : @"paused",
+            }];
     }];
     return ret;
 }
 
+- (NSDictionary*)fastStatus {
+
+    __block NSDictionary* ret = nil;
+
+    [self dispatchSyncWithReadOnlySQLTransaction:^{
+        CKKSZoneStateEntry* ckse = [CKKSZoneStateEntry state:self.zoneName];
+
+        ret = @{
+            @"view":                CKKSNilToNSNull(self.zoneName),
+            @"ckaccountstatus":     self.accountStatus == CKAccountStatusCouldNotDetermine ? @"could not determine" :
+                self.accountStatus == CKAccountStatusAvailable         ? @"logged in" :
+                self.accountStatus == CKAccountStatusRestricted        ? @"restricted" :
+                self.accountStatus == CKAccountStatusNoAccount         ? @"logged out" : @"unknown",
+            @"accounttracker":      stringify(self.accountTracker),
+            @"fetcher":             stringify(self.zoneChangeFetcher),
+            @"zoneCreated":         boolstr(ckse.ckzonecreated),
+            @"zoneSubscribed":      boolstr(ckse.ckzonesubscribed),
+            @"keystate":            CKKSNilToNSNull(self.keyHierarchyState),
+            @"statusError":         [NSNull null],
+            @"launchSequence":      CKKSNilToNSNull([self.launch eventsByTime]),
+
+            @"lastIncomingQueueOperation":         stringify(self.lastIncomingQueueOperation),
+            @"lastNewTLKOperation":                stringify(self.lastNewTLKOperation),
+            @"lastOutgoingQueueOperation":         stringify(self.lastOutgoingQueueOperation),
+            @"lastProcessReceivedKeysOperation":   stringify(self.lastProcessReceivedKeysOperation),
+            @"lastReencryptOutgoingItemsOperation":stringify(self.lastReencryptOutgoingItemsOperation),
+        };
+    }];
 
+    return ret;
+}
 
 #endif /* OCTAGON */
 @end