]> git.saurik.com Git - apple/security.git/blobdiff - OSX/sec/SOSCircle/SecureObjectSync/SOSAccount.c
Security-57740.20.22.tar.gz
[apple/security.git] / OSX / sec / SOSCircle / SecureObjectSync / SOSAccount.c
index f65550ec27e61f13cb8d8397b8717e3f4b800184..b387b6fbc88269b817431ac6a2c686a922d1fe04 100644 (file)
 #include <Security/SecureObjectSync/SOSRingUtils.h>
 #include <Security/SecureObjectSync/SOSPeerInfoSecurityProperties.h>
 #include <Security/SecureObjectSync/SOSPeerInfoV2.h>
+#include <Security/SecureObjectSync/SOSAccountTransaction.h>
 #include <Security/SecItemInternal.h>
 #include <SOSCircle/CKBridge/SOSCloudKeychainClient.h>
 #include <SOSCircle/Regressions/SOSRegressionUtilities.h>
 
+#include <utilities/SecCFWrappers.h>
+
 CFGiblisWithCompareFor(SOSAccount);
 
 const CFStringRef SOSTransportMessageTypeIDS = CFSTR("IDS");
+const CFStringRef SOSTransportMessageTypeIDSV2 = CFSTR("IDS2.0");
 const CFStringRef SOSTransportMessageTypeKVS = CFSTR("KVS");
 const CFStringRef kSOSDSIDKey = CFSTR("AccountDSID");
 const CFStringRef kSOSEscrowRecord = CFSTR("EscrowRecord");
 const CFStringRef kSOSUnsyncedViewsKey = CFSTR("unsynced");
+const CFStringRef kSOSPendingEnableViewsToBeSetKey = CFSTR("pendingEnableViews");
+const CFStringRef kSOSPendingDisableViewsToBeSetKey = CFSTR("pendingDisableViews");
 
 
 #define DATE_LENGTH 25
 const CFStringRef kSOSAccountDebugScope = CFSTR("Scope");
 
-
 bool SOSAccountEnsureFactoryCircles(SOSAccountRef a)
 {
     bool result = false;
@@ -69,24 +74,24 @@ SOSAccountRef SOSAccountCreateBasic(CFAllocatorRef allocator,
 
     a->queue = dispatch_queue_create("Account Queue", DISPATCH_QUEUE_SERIAL);
 
-    a->notification_cleanups = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
-
     a->gestalt = CFRetainSafe(gestalt);
 
     a->trusted_circle = NULL;
-    a->trusted_rings = CFDictionaryCreateMutableForCFTypes(allocator);
     a->backups = CFDictionaryCreateMutableForCFTypes(allocator);
     a->my_identity = NULL;
     a->retirees = CFSetCreateMutableForSOSPeerInfosByID(allocator);
 
     a->factory = factory; // We adopt the factory. kthanksbai.
+
+    a->isListeningForSync = false;
     
     a->_user_private = NULL;
     a->_password_tmp = NULL;
     a->user_private_timer = NULL;
+    a->lock_notification_token = NOTIFY_TOKEN_INVALID;
 
     a->change_blocks = CFArrayCreateMutableForCFTypes(allocator);
-
+    a->waitForInitialSync_blocks = NULL;
     a->departure_code = kSOSNeverAppliedToCircle;
 
     a->key_transport = (SOSTransportKeyParameterRef)SOSTransportKeyParameterKVSCreate(a, NULL);
@@ -94,10 +99,74 @@ SOSAccountRef SOSAccountCreateBasic(CFAllocatorRef allocator,
     a->kvs_message_transport = NULL;
     a->ids_message_transport = NULL;
     a->expansion = CFDictionaryCreateMutableForCFTypes(allocator);
+    SOSAccountAddRingDictionary(a);
+    
+    a->saveBlock = NULL;
+    a->circle_rings_retirements_need_attention = false;
+    a->engine_peer_state_needs_repair = false;
+    a->key_interests_need_updating = false;
+    a->deviceID = NULL;
     
     return a;
 }
 
+//
+// MARK: Transactional
+//
+
+void SOSAccountWithTransaction_Locked(SOSAccountRef account, void (^action)(SOSAccountRef account, SOSAccountTransactionRef txn)) {
+    SOSAccountTransactionRef at = SOSAccountTransactionCreate(account);
+    action(account, at);
+    SOSAccountTransactionFinish(at);
+    CFReleaseNull(at);
+}
+
+
+
+void SOSAccountWithTransaction(SOSAccountRef account, bool sync, void (^action)(SOSAccountRef account, SOSAccountTransactionRef txn)) {
+    dispatch_block_t with_transaction =  ^{
+        SOSAccountWithTransaction_Locked(account, action);
+    };
+
+    if (sync) {
+        dispatch_sync(SOSAccountGetQueue(account), with_transaction);
+    } else {
+        dispatch_async(SOSAccountGetQueue(account), with_transaction);
+    }
+}
+
+void SOSAccountWithTransactionSync(SOSAccountRef account, void (^action)(SOSAccountRef account, SOSAccountTransactionRef txn)) {
+    SOSAccountWithTransaction(account, true, action);
+}
+
+void SOSAccountWithTransactionAsync(SOSAccountRef account, bool sync, void (^action)(SOSAccountRef account, SOSAccountTransactionRef txn)) {
+    SOSAccountWithTransaction(account, false, action);
+}
+
+//
+// MARK: Save Block
+//
+
+void SOSAccountSetSaveBlock(SOSAccountRef account, SOSAccountSaveBlock saveBlock) {
+    CFAssignRetained(account->saveBlock, Block_copy(saveBlock));
+}
+
+void SOSAccountFlattenToSaveBlock(SOSAccountRef account) {
+    if (account->saveBlock) {
+        CFErrorRef localError = NULL;
+        CFDataRef saveData = SOSAccountCopyEncodedData(account, kCFAllocatorDefault, &localError);
+        
+        (account->saveBlock)(saveData, localError);
+        
+        CFReleaseNull(saveData);
+        CFReleaseNull(localError);
+    }
+}
+
+//
+// MARK: Security Properties
+//
+
 SOSSecurityPropertyResultCode SOSAccountUpdateSecurityProperty(SOSAccountRef account, CFStringRef property, SOSSecurityPropertyActionCode actionCode, CFErrorRef *error) {
     SOSSecurityPropertyResultCode retval = kSOSCCGeneralSecurityPropertyError;
     bool updateCircle = false;
@@ -141,7 +210,7 @@ bool SOSAccountUpdateGestalt(SOSAccountRef account, CFDictionaryRef new_gestalt)
     if (account->trusted_circle && account->my_identity
         && SOSFullPeerInfoUpdateGestalt(account->my_identity, new_gestalt, NULL)) {
         SOSAccountModifyCircle(account, NULL, ^(SOSCircleRef circle_to_change) {
-            secnotice("circleChange", "dCalling SOSCircleUpdatePeerInfo for gestalt change");
+            secnotice("circleChange", "Calling SOSCircleUpdatePeerInfo for gestalt change");
             return SOSCircleUpdatePeerInfo(circle_to_change, SOSAccountGetMyPeerInfo(account));
         });
     }
@@ -150,17 +219,35 @@ bool SOSAccountUpdateGestalt(SOSAccountRef account, CFDictionaryRef new_gestalt)
     return true;
 }
 
-bool SOSAccountUpdateDSID(SOSAccountRef account, CFStringRef dsid){
+static bool SOSAccountUpdateDSID(SOSAccountRef account, CFStringRef dsid){
     SOSAccountSetValue(account, kSOSDSIDKey, dsid, NULL);
     //send new DSID over account changed
     SOSTransportCircleSendOfficialDSID(account->circle_transport, dsid, NULL);
-    
     return true;
 }
 
-bool SOSAccountUpdateFullPeerInfo(SOSAccountRef account, CFSetRef minimumViews) {
+void SOSAccountAssertDSID(SOSAccountRef account, CFStringRef dsid) {
+    CFStringRef accountDSID = SOSAccountGetValue(account, kSOSDSIDKey, NULL);
+    if(accountDSID == NULL) {
+        secdebug("updates", "Setting dsid, current dsid is empty for this account: %@", dsid);
+
+        SOSAccountUpdateDSID(account, dsid);
+    } else if(CFStringCompare(dsid, accountDSID, 0) != kCFCompareEqualTo) {
+        secnotice("updates", "Changing DSID from: %@ to %@", accountDSID, dsid);
+
+        //DSID has changed, blast the account!
+        SOSAccountSetToNew(account);
+
+        //update DSID to the new DSID
+        SOSAccountUpdateDSID(account, dsid);
+    } else {
+        secnotice("updates", "Not Changing DSID: %@ to %@", accountDSID, dsid);
+    }
+}
+
+bool SOSAccountUpdateFullPeerInfo(SOSAccountRef account, CFSetRef minimumViews, CFSetRef excludedViews) {
     if (account->trusted_circle && account->my_identity) {
-        if(SOSFullPeerInfoUpdateToCurrent(account->my_identity, minimumViews)) {
+        if(SOSFullPeerInfoUpdateToCurrent(account->my_identity, minimumViews, excludedViews)) {
             SOSAccountModifyCircle(account, NULL, ^(SOSCircleRef circle_to_change) {
                 secnotice("circleChange", "Calling SOSCircleUpdatePeerInfo for gestalt change");
                 return SOSCircleUpdatePeerInfo(circle_to_change, SOSFullPeerInfoGetPeerInfo(account->my_identity));
@@ -171,9 +258,67 @@ bool SOSAccountUpdateFullPeerInfo(SOSAccountRef account, CFSetRef minimumViews)
     return true;
 }
 
+static bool SOSAccountValueSetContainsValue(SOSAccountRef account, CFStringRef key, CFTypeRef value) {
+    CFSetRef foundSet = asSet(SOSAccountGetValue(account, key, NULL), NULL);
+    return foundSet && CFSetContainsValue(foundSet, value);
+}
+
+static void SOSAccountValueUnionWith(SOSAccountRef account, CFStringRef key, CFSetRef valuesToUnion) {
+    CFMutableSetRef unionedSet = CFSetCreateMutableCopy(kCFAllocatorDefault, 0, valuesToUnion);
+    CFSetRef foundSet = asSet(SOSAccountGetValue(account, key, NULL), NULL);
+    if (foundSet) {
+        CFSetUnion(unionedSet, foundSet);
+    }
+    SOSAccountSetValue(account, key, unionedSet, NULL);
+    CFReleaseNull(unionedSet);
+}
+
+static void SOSAccountValueSubtractFrom(SOSAccountRef account, CFStringRef key, CFSetRef valuesToSubtract) {
+    CFSetRef foundSet = asSet(SOSAccountGetValue(account, key, NULL), NULL);
+    if (foundSet) {
+        CFMutableSetRef subtractedSet = CFSetCreateMutableCopy(kCFAllocatorDefault, 0, foundSet);
+        CFSetSubtract(subtractedSet, valuesToSubtract);
+        SOSAccountSetValue(account, key, subtractedSet, NULL);
+        CFReleaseNull(subtractedSet);
+    }
+}
+
+void SOSAccountPendEnableViewSet(SOSAccountRef account, CFSetRef enabledViews)
+{
+    if(CFSetGetValue(enabledViews, kSOSViewKeychainV0) != NULL) secnotice("viewChange", "Warning, attempting to Add KeychainV0");
+
+    SOSAccountValueUnionWith(account, kSOSPendingEnableViewsToBeSetKey, enabledViews);
+    SOSAccountValueSubtractFrom(account, kSOSPendingDisableViewsToBeSetKey, enabledViews);
+}
+
+
+void SOSAccountPendDisableViewSet(SOSAccountRef account, CFSetRef disabledViews)
+{
+    SOSAccountValueUnionWith(account, kSOSPendingDisableViewsToBeSetKey, disabledViews);
+    SOSAccountValueSubtractFrom(account, kSOSPendingEnableViewsToBeSetKey, disabledViews);
+}
+
+static SOSViewResultCode SOSAccountVirtualV0Behavior(SOSAccountRef account, SOSViewActionCode actionCode) {
+    SOSViewResultCode retval = kSOSCCGeneralViewError;
+    // The V0 view switches on and off all on it's own, we allow people the delusion
+    // of control and status if it's what we're stuck at., otherwise error.
+    if (SOSAccountSyncingV0(account)) {
+        require_action_quiet(actionCode == kSOSCCViewDisable, errOut, CFSTR("Can't disable V0 view and it's on right now"));
+        retval = kSOSCCViewMember;
+    } else {
+        require_action_quiet(actionCode == kSOSCCViewEnable, errOut, CFSTR("Can't enable V0 and it's off right now"));
+        retval = kSOSCCViewNotMember;
+    }
+errOut:
+    return retval;
+}
+
+
 SOSViewResultCode SOSAccountUpdateView(SOSAccountRef account, CFStringRef viewname, SOSViewActionCode actionCode, CFErrorRef *error) {
     SOSViewResultCode retval = kSOSCCGeneralViewError;
     SOSViewResultCode currentStatus = kSOSCCGeneralViewError;
+    bool alreadyInSync = SOSAccountHasCompletedInitialSync(account);
+
     bool updateCircle = false;
     require_action_quiet(account->trusted_circle, errOut, SOSCreateError(kSOSErrorNoCircle, CFSTR("No Trusted Circle"), NULL, error));
     require_action_quiet(account->my_identity, errOut, SOSCreateError(kSOSErrorPeerNotFound, CFSTR("No Peer for Account"), NULL, error));
@@ -182,29 +327,38 @@ SOSViewResultCode SOSAccountUpdateView(SOSAccountRef account, CFStringRef viewna
     require_action_quiet((currentStatus == kSOSCCViewNotMember) || (currentStatus == kSOSCCViewMember), errOut, CFSTR("View Membership Not Actionable"));
 
     if (CFEqualSafe(viewname, kSOSViewKeychainV0)) {
-        // The V0 view switches on and off all on it's own, we allow people the delusion
-        // of control and status if it's what we're stuck at., otherwise error.
-        if (SOSAccountSyncingV0(account)) {
-            require_action_quiet(actionCode = kSOSCCViewDisable, errOut, CFSTR("Can't disable V0 view and it's on right now"));
-            retval = kSOSCCViewMember;
-        } else {
-            require_action_quiet(actionCode = kSOSCCViewEnable, errOut, CFSTR("Can't enable V0 and it's off right now"));
-            retval = kSOSCCViewNotMember;
-        }
+        retval = SOSAccountVirtualV0Behavior(account, actionCode);
     } else if (SOSAccountSyncingV0(account) && SOSViewsIsV0Subview(viewname)) {
         // Subviews of V0 syncing can't be turned off if V0 is on.
         require_action_quiet(actionCode = kSOSCCViewDisable, errOut, CFSTR("Have V0 peer can't disable"));
         retval = kSOSCCViewMember;
     } else {
+        CFMutableSetRef pendingSet = CFSetCreateMutableForCFTypes(kCFAllocatorDefault);
+        CFSetAddValue(pendingSet, viewname);
+
         if(actionCode == kSOSCCViewEnable && currentStatus == kSOSCCViewNotMember) {
-            retval = SOSFullPeerInfoUpdateViews(account->my_identity, actionCode, viewname, error);
-            if(retval == kSOSCCViewMember) updateCircle = true;
+            if(alreadyInSync) {
+                retval = SOSFullPeerInfoUpdateViews(account->my_identity, actionCode, viewname, error);
+                if(retval == kSOSCCViewMember) updateCircle = true;
+            } else {
+                SOSAccountPendEnableViewSet(account, pendingSet);
+                retval = kSOSCCViewMember;
+                updateCircle = false;
+            }
         } else if(actionCode == kSOSCCViewDisable && currentStatus == kSOSCCViewMember) {
-            retval = SOSFullPeerInfoUpdateViews(account->my_identity, actionCode, viewname, error);
-            if(retval == kSOSCCViewNotMember) updateCircle = true;
+            if(alreadyInSync) {
+                retval = SOSFullPeerInfoUpdateViews(account->my_identity, actionCode, viewname, error);
+                if(retval == kSOSCCViewNotMember) updateCircle = true;
+            } else {
+                SOSAccountPendDisableViewSet(account, pendingSet);
+                retval = kSOSCCViewNotMember;
+                updateCircle = false;
+            }
         } else {
             retval = currentStatus;
         }
+        
+        CFReleaseNull(pendingSet);
 
         if (updateCircle) {
             SOSAccountModifyCircle(account, NULL, ^(SOSCircleRef circle_to_change) {
@@ -223,7 +377,13 @@ SOSViewResultCode SOSAccountViewStatus(SOSAccountRef account, CFStringRef viewna
     require_action_quiet(account->trusted_circle, errOut, SOSCreateError(kSOSErrorNoCircle, CFSTR("No Trusted Circle"), NULL, error));
     require_action_quiet(account->my_identity, errOut, SOSCreateError(kSOSErrorPeerNotFound, CFSTR("No Peer for Account"), NULL, error));
 
-    retval = SOSFullPeerInfoViewStatus(account->my_identity, viewname, error);
+    if (SOSAccountValueSetContainsValue(account, kSOSPendingEnableViewsToBeSetKey, viewname)) {
+        retval = kSOSCCViewMember;
+    } else if (SOSAccountValueSetContainsValue(account, kSOSPendingDisableViewsToBeSetKey, viewname)) {
+        retval = kSOSCCViewNotMember;
+    } else {
+        retval = SOSFullPeerInfoViewStatus(account->my_identity, viewname, error);
+    }
 
     // If that doesn't say we're a member and this view is a V0 subview, and we're syncing V0 views we are a member
     if (retval != kSOSCCViewMember) {
@@ -247,49 +407,93 @@ errOut:
 
 static void dumpViewSet(CFStringRef label, CFSetRef views) {
     if(views) {
-        secnotice("circleChange", "%@ list: %@", label, views);
+        CFStringSetPerformWithDescription(views, ^(CFStringRef description) {
+            secnotice("circleChange", "%@ list: %@", label, description);
+        });
     } else {
         secnotice("circleChange", "No %@ list provided.", label);
     }
 }
 
-bool SOSAccountUpdateViewSets(SOSAccountRef account, CFSetRef enabledViews, CFSetRef disabledViews) {
+static bool SOSAccountScreenViewListForValidV0(SOSAccountRef account, CFMutableSetRef viewSet, SOSViewActionCode actionCode) {
+    bool retval = true;
+    if(viewSet && CFSetContainsValue(viewSet, kSOSViewKeychainV0)) {
+        retval = SOSAccountVirtualV0Behavior(account, actionCode) != kSOSCCGeneralViewError;
+        CFSetRemoveValue(viewSet, kSOSViewKeychainV0);
+    }
+    return retval;
+}
+
+bool SOSAccountUpdateViewSets(SOSAccountRef account, CFSetRef origEnabledViews, CFSetRef origDisabledViews) {
+    bool retval = false;
     bool updateCircle = false;
+    SOSPeerInfoRef  pi = NULL;
+    CFMutableSetRef enabledViews = NULL;
+    CFMutableSetRef disabledViews = NULL;
+    if(origEnabledViews) enabledViews = CFSetCreateMutableCopy(kCFAllocatorDefault, 0, origEnabledViews);
+    if(origDisabledViews) disabledViews = CFSetCreateMutableCopy(kCFAllocatorDefault, 0, origDisabledViews);
     dumpViewSet(CFSTR("Enabled"), enabledViews);
     dumpViewSet(CFSTR("Disabled"), disabledViews);
     
     require_action_quiet(account->trusted_circle, errOut, secnotice("views", "Attempt to set viewsets with no trusted circle"));
-    require_action_quiet(account->my_identity, errOut, secnotice("views", "Attempt to set viewsets with no fullPeerInfo"));
-    require_action_quiet(enabledViews || disabledViews, errOut, secnotice("views", "No work to do"));
     
-    // Copy my views
+    // Make sure we have a peerInfo capable of supporting views.
     SOSFullPeerInfoRef fpi = SOSAccountGetMyFullPeerInfo(account);
-    SOSPeerInfoRef  pi = SOSPeerInfoCreateCopy(kCFAllocatorDefault, SOSFullPeerInfoGetPeerInfo(fpi), NULL);
+    require_action_quiet(fpi, errOut, secnotice("views", "Attempt to set viewsets with no fullPeerInfo"));
+    require_action_quiet(enabledViews || disabledViews, errOut, secnotice("views", "No work to do"));
+
+    pi = SOSPeerInfoCreateCopy(kCFAllocatorDefault, SOSFullPeerInfoGetPeerInfo(fpi), NULL);
     
     require_action_quiet(pi, errOut, secnotice("views", "Couldn't copy PeerInfoRef"));
     
-    
     if(!SOSPeerInfoVersionIsCurrent(pi)) {
-        if(!SOSPeerInfoUpdateToV2(pi, NULL)) {
-            secnotice("views", "Unable to update peer to V2- can't update views");
-            return false;
-        }
+        CFErrorRef updateFailure = NULL;
+        require_action_quiet(SOSPeerInfoUpdateToV2(pi, &updateFailure), errOut,
+                             (secnotice("views", "Unable to update peer to V2- can't update views: %@", updateFailure), (void) CFReleaseNull(updateFailure)));
+        secnotice("V2update", "Updating PeerInfo to V2 within SOSAccountUpdateViewSets");
+        updateCircle = true;
     }
     
-    if(enabledViews) updateCircle = SOSViewSetEnable(pi, enabledViews);
-    if(disabledViews) updateCircle |= SOSViewSetDisable(pi, disabledViews);
+    CFStringSetPerformWithDescription(enabledViews, ^(CFStringRef description) {
+        secnotice("viewChange", "Enabling %@", description);
+    });
     
-    /* UPDATE FULLPEERINFO VIEWS */
+    CFStringSetPerformWithDescription(disabledViews, ^(CFStringRef description) {
+        secnotice("viewChange", "Disabling %@", description);
+    });
     
-    if (updateCircle && SOSFullPeerInfoUpdateToThisPeer(fpi, pi, NULL)) {
-        SOSAccountModifyCircle(account, NULL, ^(SOSCircleRef circle_to_change) {
-            secnotice("circleChange", "Calling SOSCircleUpdatePeerInfo for views change");
-            return SOSCircleUpdatePeerInfo(circle_to_change, SOSFullPeerInfoGetPeerInfo(account->my_identity));
-        });
+    require_action_quiet(SOSAccountScreenViewListForValidV0(account, enabledViews, kSOSCCViewEnable), errOut, secnotice("viewChange", "Bad view change (enable) with kSOSViewKeychainV0"));
+    require_action_quiet(SOSAccountScreenViewListForValidV0(account, disabledViews, kSOSCCViewDisable), errOut, secnotice("viewChange", "Bad view change (disable) with kSOSViewKeychainV0"));
+
+    if(SOSAccountHasCompletedInitialSync(account)) {
+        if(enabledViews) updateCircle |= SOSViewSetEnable(pi, enabledViews);
+        if(disabledViews) updateCircle |= SOSViewSetDisable(pi, disabledViews);
+        retval = true;
+    } else {
+        //hold on to the views and enable them later
+        if(enabledViews) SOSAccountPendEnableViewSet(account, enabledViews);
+        if(disabledViews) SOSAccountPendDisableViewSet(account, disabledViews);
+        retval = true;
     }
     
+    if(updateCircle) {
+        /* UPDATE FULLPEERINFO VIEWS */
+        require_quiet(SOSFullPeerInfoUpdateToThisPeer(fpi, pi, NULL), errOut);
+        
+        require_quiet(SOSAccountModifyCircle(account, NULL, ^(SOSCircleRef circle_to_change) {
+            secnotice("circleChange", "Calling SOSCircleUpdatePeerInfo for views or peerInfo change");
+            return SOSCircleUpdatePeerInfo(circle_to_change, SOSFullPeerInfoGetPeerInfo(account->my_identity));
+        }), errOut);
+        
+        // Make sure we update the engine
+        account->circle_rings_retirements_need_attention = true;
+    }
+
 errOut:
-    return updateCircle;
+    CFReleaseNull(enabledViews);
+    CFReleaseNull(disabledViews);
+    CFReleaseNull(pi);
+    return retval;
 }
 
 
@@ -300,8 +504,8 @@ SOSAccountRef SOSAccountCreate(CFAllocatorRef allocator,
 
     SOSAccountEnsureFactoryCircles(a);
 
-    SOSUpdateKeyInterest(a);
-
+    a->key_interests_need_updating = true;
+    
     return a;
 }
 
@@ -312,7 +516,7 @@ static void SOSAccountDestroy(CFTypeRef aObj) {
     //    Don't free it.
     //   a->factory
 
-    SOSAccountCleanupNotificationForAllPeers(a);
+    SOSAccountCancelSyncChecking(a);
 
     SOSEngineRef engine = SOSDataSourceFactoryGetEngineForDataSourceName(a->factory, SOSCircleGetName(a->trusted_circle), NULL);
 
@@ -324,7 +528,6 @@ static void SOSAccountDestroy(CFTypeRef aObj) {
 
         CFReleaseNull(a->my_identity);
         CFReleaseNull(a->trusted_circle);
-        CFReleaseNull(a->trusted_rings);
         CFReleaseNull(a->backups);
         CFReleaseNull(a->retirees);
 
@@ -343,12 +546,14 @@ static void SOSAccountDestroy(CFTypeRef aObj) {
         CFReleaseNull(a->key_transport);
         CFReleaseNull(a->circle_transport);
         dispatch_release(a->queue);
-        CFReleaseNull(a->notification_cleanups);
 
         dispatch_release(a->user_private_timer);
         CFReleaseNull(a->change_blocks);
+        CFReleaseNull(a->waitForInitialSync_blocks);
         CFReleaseNull(a->expansion);
 
+        CFReleaseNull(a->saveBlock);
+        CFReleaseNull(a->deviceID);
     });
 }
 
@@ -434,13 +639,22 @@ do_keychain_delete_sbd()
     return result;
 }
 
+void static SOSAccountResetKeyInterests(SOSAccountRef a) {
+    CFDictionaryRef emptyDictionary = CFDictionaryCreateForCFTypes(kCFAllocatorDefault, NULL);
+    SOSCloudKeychainUpdateKeys(emptyDictionary, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(CFDictionaryRef returnedValues, CFErrorRef error) {
+        if (error) {
+            secerror("Error updating keys: %@", error);
+        }
+    });
+    CFReleaseNull(emptyDictionary);
+}
+
 void SOSAccountSetToNew(SOSAccountRef a) {
     secnotice("accountChange", "Setting Account to New");
     int result = 0;
     
     CFReleaseNull(a->my_identity);
     CFReleaseNull(a->trusted_circle);
-    CFReleaseNull(a->trusted_rings);
     CFReleaseNull(a->backups);
     CFReleaseNull(a->retirees);
 
@@ -455,24 +669,35 @@ void SOSAccountSetToNew(SOSAccountRef a) {
     CFReleaseNull(a->kvs_message_transport);
     CFReleaseNull(a->ids_message_transport);
     CFReleaseNull(a->expansion);
+    CFReleaseNull(a->deviceID);
     
     /* remove all syncable items */
-    result = do_keychain_delete_aks_bags();
+    result = do_keychain_delete_aks_bags(); (void) result;
     secdebug("set to new", "result for deleting aks bags: %d", result);
 
-    result = do_keychain_delete_identities();
+    result = do_keychain_delete_identities(); (void) result;
     secdebug("set to new", "result for deleting identities: %d", result);
  
-    result = do_keychain_delete_lakitu();
+    result = do_keychain_delete_lakitu(); (void) result;
     secdebug("set to new", "result for deleting lakitu: %d", result);
     
-    result = do_keychain_delete_sbd();
+    result = do_keychain_delete_sbd(); (void) result;
     secdebug("set to new", "result for deleting sbd: %d", result);
 
     a->user_public_trusted = false;
     a->departure_code = kSOSNeverAppliedToCircle;
-    a->user_private_timer = 0;
-    a->lock_notification_token = 0;
+
+    if (a->user_private_timer) {
+        dispatch_source_cancel(a->user_private_timer);
+        dispatch_release(a->user_private_timer);
+        a->user_private_timer = NULL;
+        xpc_transaction_end();
+
+    }
+    if (a->lock_notification_token != NOTIFY_TOKEN_INVALID) {
+        notify_cancel(a->lock_notification_token);
+        a->lock_notification_token = NOTIFY_TOKEN_INVALID;
+    }
 
     // keeping gestalt;
     // keeping factory;
@@ -486,24 +711,46 @@ void SOSAccountSetToNew(SOSAccountRef a) {
     a->kvs_message_transport = NULL;
     a->ids_message_transport = NULL;
 
-    a->trusted_rings = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
     a->backups = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
 
     a->retirees = CFSetCreateMutableForSOSPeerInfosByID(kCFAllocatorDefault);
     a->expansion = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
+    SOSAccountAddRingDictionary(a);
 
     SOSAccountEnsureFactoryCircles(a); // Does rings too
 
-    SOSUpdateKeyInterest(a);
+    // Reset our key interests since we are new, we need to hear about everything:
+    SOSAccountResetKeyInterests(a);
+
+    a->key_interests_need_updating = true;
 }
 
+bool SOSAccountIsNew(SOSAccountRef account, CFErrorRef *error){
+    bool result = false;
+    require_quiet(account->user_public_trusted == false, exit);
+    require_quiet(account->departure_code == kSOSNeverAppliedToCircle, exit);
+    require_quiet(account->user_private_timer == NULL, exit);
+    require_quiet(account->lock_notification_token == NOTIFY_TOKEN_INVALID, exit);
+    require_quiet (CFDictionaryGetCount(account->backups) == 0, exit);
+    require_quiet(CFSetGetCount(account->retirees) == 0, exit);
+
+    result = true;
+exit:
+    return result;
+}
 
 static CFStringRef SOSAccountCopyFormatDescription(CFTypeRef aObj, CFDictionaryRef formatOptions) {
     SOSAccountRef a = (SOSAccountRef) aObj;
     
     CFStringRef gestaltDescription = CFDictionaryCopyCompactDescription(a->gestalt);
 
-    CFStringRef result = CFStringCreateWithFormat(NULL, NULL, CFSTR("<SOSAccount@%p: Gestalt: %@ Circle: %@ Me: %@>"), a, gestaltDescription, a->trusted_circle, a->my_identity);
+    CFStringRef result = CFStringCreateWithFormat(NULL, NULL, CFSTR("<SOSAccount@%p: %c%c%c%c%c G: %@ Me: %@ C: %@ >"), a,
+                                                  a->user_public ? 'P' : 'p',
+                                                  a->user_public_trusted ? 'T' : 't',
+                                                  a->isListeningForSync ? 'L' : 'l',
+                                                  SOSAccountHasCompletedInitialSync(a) ? 'C' : 'c',
+                                                  SOSAccountHasCompletedRequiredBackupSync(a) ? 'B' : 'b',
+                                                  gestaltDescription, a->my_identity, a->trusted_circle);
 
     CFReleaseNull(gestaltDescription);
 
@@ -528,7 +775,7 @@ static Boolean SOSAccountCompare(CFTypeRef lhs, CFTypeRef rhs)
 
     return CFEqualSafe(laccount->gestalt, raccount->gestalt)
         && CFEqualSafe(laccount->trusted_circle, raccount->trusted_circle)
-        && CFEqualSafe(laccount->trusted_rings, raccount->trusted_rings)
+        && CFEqualSafe(laccount->expansion, raccount->expansion)
         && CFEqualSafe(laccount->my_identity, raccount->my_identity);
 }
 
@@ -548,7 +795,7 @@ SOSFullPeerInfoRef SOSAccountCopyAccountIdentityPeerInfo(SOSAccountRef account,
 static bool SOSAccountThisDeviceCanSyncWithCircle(SOSAccountRef account) {
     bool ok = false;
     __block CFErrorRef error = NULL;
-
+    
     if (!SOSAccountHasPublicKey(account, &error)) {
         CFReleaseSafe(error);
         return false;
@@ -559,29 +806,7 @@ static bool SOSAccountThisDeviceCanSyncWithCircle(SOSAccountRef account) {
     require_action_quiet(account->my_identity, xit,
                          SOSCreateError(kSOSErrorBadFormat, CFSTR("Account identity not set"), NULL, &error));
     
-    CFStringRef deviceID = SOSPeerInfoCopyDeviceID(SOSFullPeerInfoGetPeerInfo(account->my_identity));
-    if(deviceID == NULL || CFStringGetLength(deviceID) == 0){
-        hasID = false;
-        secerror("Cannot sync with all peers at this time, securityd needs the IDS device ID first.");
-        
-        __block bool success = true;
-        
-        SOSCloudKeychainGetIDSDeviceID(^(CFDictionaryRef returnedValues, CFErrorRef sync_error){
-            success = (sync_error == NULL);
-            if (!success) {
-                CFRetainAssign(error, sync_error);
-            }
-        });
-        
-        if(!success){
-            secerror("Could not ask IDSKeychainSyncingProxy for Device ID: %@", error);
-        }
-        else{
-            secdebug("IDS Transport", "Attempting to retrieve the IDS Device ID");
-        }
-    }
-    CFReleaseNull(deviceID);
-
+    SOSTransportMessageIDSGetIDSDeviceID(account);    
     
     require_action_quiet(account->trusted_circle, xit,
                          SOSCreateError(kSOSErrorBadFormat, CFSTR("Account trusted circle not set"), NULL, &error));
@@ -605,67 +830,240 @@ static bool SOSAccountIsThisPeerIDMe(SOSAccountRef account, CFStringRef peerID)
     return myPeerID && CFEqualSafe(myPeerID, peerID);
 }
 
-static bool isDefaultsWriteSetupToSyncOverIDS(){
-    return ((whichTransportType == kSOSTransportIDS || whichTransportType == kSOSTransportFuture || whichTransportType == kSOSTransportPresent));
-}
-
-bool SOSAccountSyncWithAllPeers(SOSAccountRef account, CFErrorRef *error)
-{
+bool SOSAccountSendIKSPSyncList(SOSAccountRef account, CFErrorRef *error){
     bool result = true;
-    __block bool SyncingCompletedOverIDS = true;
-    __block bool SyncingCompletedOverKVS = true;
     __block CFErrorRef localError = NULL;
-    SOSCircleRef circle  = SOSAccountGetCircle(account, error);
-    CFMutableDictionaryRef circleToPeerIDs = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
-    CFMutableArrayRef peerIds = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
-    
+    __block CFMutableArrayRef ids = NULL;
+    SOSCircleRef circle = NULL;
+
+    require_action_quiet(SOSAccountIsInCircle(account, NULL), xit,
+                         SOSCreateError(kSOSErrorNoCircle, CFSTR("This device is not in circle"),
+                                        NULL, &localError));
+
+    circle  = SOSAccountGetCircle(account, error);
+    ids = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
+
     require_action_quiet(SOSAccountThisDeviceCanSyncWithCircle(account), xit,
                          SOSCreateError(kSOSErrorNoCircle, CFSTR("This device cannot sync with circle"),
                                         NULL, &localError));
 
     SOSCircleForEachValidPeer(circle, account->user_public, ^(SOSPeerInfoRef peer) {
         if (!SOSAccountIsThisPeerIDMe(account, SOSPeerInfoGetPeerID(peer))) {
-            if (isDefaultsWriteSetupToSyncOverIDS() && SOSPeerInfoShouldUseIDSTransport(SOSFullPeerInfoGetPeerInfo(account->my_identity), peer)) {
-                secdebug("IDS Transport", "Syncing with IDS capable peers using IDS!");
-                CFMutableDictionaryRef circleToIdsId = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
-                CFMutableArrayRef ids = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
-                CFArrayAppendValue(ids, SOSPeerInfoGetPeerID(peer));
-                CFDictionaryAddValue(circleToIdsId, SOSCircleGetName(circle), ids);
-                SyncingCompletedOverIDS = SOSTransportMessageSyncWithPeers(account->ids_message_transport, circleToIdsId, &localError);
-                CFReleaseNull(circleToIdsId);
-            } else {
-                CFArrayAppendValue(peerIds, SOSPeerInfoGetPeerID(peer));
+            if(SOSPeerInfoShouldUseIDSTransport(SOSFullPeerInfoGetPeerInfo(account->my_identity), peer) &&
+               SOSPeerInfoShouldUseIDSMessageFragmentation(SOSFullPeerInfoGetPeerInfo(account->my_identity), peer)){
+                SOSTransportMessageIDSSetFragmentationPreference(account->ids_message_transport, kCFBooleanTrue);
+                CFStringRef deviceID = SOSPeerInfoCopyDeviceID(peer);
+                if(deviceID != NULL){
+                    CFArrayAppendValue(ids, deviceID);
+                }
+                CFReleaseNull(deviceID);
+            }
+        }
+    });
+    require_quiet(CFArrayGetCount(ids) != 0, xit);
+    secnotice("IDS Transport", "List of IDS Peers to ping: %@", ids);
+
+    SOSCloudKeychainGetIDSDeviceAvailability(ids, SOSAccountGetMyPeerID(account), dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(CFDictionaryRef returnedValues, CFErrorRef sync_error) {
+        bool success = (sync_error == NULL);
+        if(!success)
+            secerror("Failed to send list of IDS peers to IDSKSP: %@", sync_error);
+    });
+xit:
+    if(error && *error != NULL)
+        secerror("SOSAccountSendIKSPSyncList had an error: %@", *error);
+
+    if(localError)
+        secerror("SOSAccountSendIKSPSyncList had an error: %@", localError);
+
+    CFReleaseNull(ids);
+    CFReleaseNull(localError);
+
+    return result;
+}
+
+bool SOSAccountSyncWithAllKVSPeers(SOSAccountRef account, CFErrorRef *error)
+{
+    __block bool result = true;
+    
+    if(SOSAccountIsInCircle(account, NULL)) {
+        SOSCircleForEachValidPeer(account->trusted_circle, account->user_public, ^(SOSPeerInfoRef peer) {
+            if (!SOSAccountIsThisPeerIDMe(account, SOSPeerInfoGetPeerID(peer))) {
+                CFStringRef deviceID = SOSPeerInfoCopyDeviceID(peer);
+                if(deviceID == NULL || !SOSPeerInfoShouldUseIDSTransport(SOSFullPeerInfoGetPeerInfo(account->my_identity), peer)){
+                    result = SOSAccountSyncWithKVSPeer(account, SOSPeerInfoGetPeerID(peer), error);
+                    if(result){
+                        secnotice("KVS Transport", "synced with peer: %@", SOSPeerInfoGetPeerID(peer));
+                    }
+                    else{
+                        secnotice("KVS Transport", "failed to sync with peer: %@", SOSPeerInfoGetPeerID(peer));
+                    }
+                }
+                CFReleaseNull(deviceID);
             }
+        });
+    }
+    secnotice("sync", "SOSAccountSyncWithAllKVSPeers returns: %d", result);
+    return true;
+}
+
+static CFMutableArrayRef SOSAccountCopyPeerIDsForDSID(SOSAccountRef account, CFStringRef deviceID, CFErrorRef* error) {
+    CFMutableArrayRef peerIDs = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
+
+    SOSCircleForEachValidPeer(account->trusted_circle, account->user_public, ^(SOSPeerInfoRef peer) {
+        CFStringRef peerDeviceID = SOSPeerInfoCopyDeviceID(peer);
+        if(peerDeviceID != NULL && CFStringCompare(peerDeviceID, deviceID, 0) == 0){
+            CFArrayAppendValue(peerIDs, SOSPeerInfoGetPeerID(peer));
         }
+        CFReleaseNull(peerDeviceID);
     });
-    if (CFArrayGetCount(peerIds)) {
-        secnotice("KVS", "Syncing with KVS capable peers");
-        CFDictionarySetValue(circleToPeerIDs, SOSCircleGetName(circle), peerIds);
-        SyncingCompletedOverKVS &= SOSTransportMessageSyncWithPeers(account->kvs_message_transport, circleToPeerIDs, &localError);
+
+    if (peerIDs == NULL || CFArrayGetCount(peerIDs) == 0) {
+        CFReleaseNull(peerIDs);
+        SOSErrorCreate(kSOSErrorPeerNotFound, error, NULL, CFSTR("No peer with DSID: %@"), deviceID);
     }
 
-    SOSEngineRef engine = SOSTransportMessageGetEngine(account->kvs_message_transport);
-    result = SOSEngineSyncWithPeers(engine, account->ids_message_transport, account->kvs_message_transport, &localError);
+    return peerIDs;
+}
+
+static bool SOSAccountSyncWithKVSPeers(SOSAccountRef account, CFArrayRef peerIDs, CFErrorRef *error) {
+    CFDictionaryRef circleToPeerIDs = NULL;
+
+    CFErrorRef localError = NULL;
+    bool result = false;
+    require_action_quiet(SOSAccountThisDeviceCanSyncWithCircle(account), xit,
+                         SOSCreateError(kSOSErrorNoCircle, CFSTR("This device cannot sync with circle"),
+                                        NULL, &localError));
+
+    circleToPeerIDs = CFDictionaryCreateForCFTypes(kCFAllocatorDefault,
+                                                   SOSCircleGetName(account->trusted_circle), peerIDs,
+                                                   NULL);
+    result = SOSTransportMessageSyncWithPeers(account->kvs_message_transport, circleToPeerIDs, &localError);
 
-    result &= ((SyncingCompletedOverIDS) &&
-               (SyncingCompletedOverKVS || (CFDictionaryGetCount(circleToPeerIDs) == 0)));
+    SOSEngineRef engine = SOSTransportMessageGetEngine(account->kvs_message_transport);
+    result &= SOSEngineSyncWithPeers(engine, &localError);
 
     if (result)
         SetCloudKeychainTraceValueForKey(kCloudKeychainNumberOfTimesSyncedWithPeers, 1);
 
 xit:
     CFReleaseNull(circleToPeerIDs);
+    if (!result) {
+        // Tell account to update SOSEngine with current trusted peers
+        if (isSOSErrorCoded(localError, kSOSErrorPeerNotFound)) {
+            secnotice("Account", "Arming account to update SOSEngine with current trusted peers");
+            account->engine_peer_state_needs_repair = true;
+        }
+        CFErrorPropagate(localError, error);
+        localError = NULL;
+    }
+    return result;
+
+}
+
+bool SOSAccountSyncWithKVSUsingIDSID(SOSAccountRef account, CFStringRef deviceID, CFErrorRef *error) {
+    bool result = false;
+    CFErrorRef localError = NULL;
+
+    secnotice("KVS Transport","Syncing with KVS capable peer via DSID: %@", deviceID);
+
+    CFArrayRef peerIDs = SOSAccountCopyPeerIDsForDSID(account, deviceID, &localError);
+    require_quiet(peerIDs, xit);
+
+    CFStringArrayPerfromWithDescription(peerIDs, ^(CFStringRef peerIDList) {
+        secnotice("KVS Transport", "Syncing with KVS capable peers: %@", peerIDList);
+    });
+
+    result = SOSAccountSyncWithKVSPeers(account, peerIDs, &localError);
+    secerror("KVS sync %s. (%@)", result ? "succeeded" : "failed", localError);
+
+xit:
+    CFReleaseNull(peerIDs);
+    CFErrorPropagate(localError, error);
+
+    return result;
+}
+
+bool SOSAccountSyncWithKVSPeer(SOSAccountRef account, CFStringRef peerID, CFErrorRef *error)
+{
+    bool result = false;
+    CFErrorRef localError = NULL;
+
+    secnotice("KVS Transport","Syncing with KVS capable peer: %@", peerID);
+
+    CFArrayRef peerIDs = CFArrayCreateForCFTypes(kCFAllocatorDefault, peerID, NULL);
+
+    result = SOSAccountSyncWithKVSPeers(account, peerIDs, &localError);
+    secerror("KVS sync %s. (%@)", result ? "succeeded" : "failed", localError);
+
+    CFReleaseNull(peerIDs);
+    CFErrorPropagate(localError, error);
+
+    return result;
+}
+
+#define LOG_ENGINE_STATE_INTERVAL 20
 
+bool SOSAccountSyncWithIDSPeer(SOSAccountRef account, CFStringRef deviceID, CFErrorRef *error)
+{
+    CFErrorRef localError = NULL;
+    CFMutableDictionaryRef circleToPeerIDs = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
+    CFMutableArrayRef ids = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
+    static int engineLogCountDown = 0;
+    bool result = true;
+    
+    require_action_quiet(SOSAccountThisDeviceCanSyncWithCircle(account), xit,
+                         SOSCreateError(kSOSErrorNoCircle, CFSTR("This device cannot sync with circle"),
+                                        NULL, &localError));
+    SOSCircleForEachValidPeer(account->trusted_circle, account->user_public, ^(SOSPeerInfoRef peer) {
+        CFStringRef peerDeviceID = SOSPeerInfoCopyDeviceID(peer);
+        if(peerDeviceID != NULL && CFStringCompare(peerDeviceID, deviceID, 0) == 0){
+            CFArrayAppendValue(ids, SOSPeerInfoGetPeerID(peer));
+        }
+        CFReleaseNull(peerDeviceID);
+    });
+    
+    require_action_quiet(CFArrayGetCount(ids), xit, SOSCreateError(kSOSErrorNoCircle, CFSTR("Cannot find peer in circle"),
+                                                                   NULL, &localError));
+    
+    secnotice("IDS Transport","Syncing with IDS capable peer: %@", ids);
+    CFDictionarySetValue(circleToPeerIDs, SOSCircleGetName(account->trusted_circle), ids);
+    result = SOSTransportMessageSyncWithPeers(account->ids_message_transport, circleToPeerIDs, &localError);
+    secnotice("IDS Transport", "IDS Sync result: %d", result);
+
+    SOSEngineRef engine = SOSTransportMessageGetEngine(account->ids_message_transport);
+    result &= SOSEngineSyncWithPeers(engine, &localError);
+    
+    if (result)
+        SetCloudKeychainTraceValueForKey(kCloudKeychainNumberOfTimesSyncedWithPeers, 1);
+    
+    if(engineLogCountDown <= 0) {
+        SOSEngineLogState(engine);
+        engineLogCountDown = LOG_ENGINE_STATE_INTERVAL;
+    } else {
+        engineLogCountDown--;
+    }
+
+xit:
+    CFReleaseNull(circleToPeerIDs);
+    
     if (!result) {
-        secdebug("Account", "Could not sync with all peers: %@", localError);
+        secdebug("Account", "Could not sync with peer %@, error: %@", deviceID, localError);
+        // Tell account to update SOSEngine with current trusted peers
+        if (isSOSErrorCoded(localError, kSOSErrorPeerNotFound)) {
+            secnotice("Account", "Arming account to update SOSEngine with current trusted peers");
+            account->engine_peer_state_needs_repair = true;
+        }
         CFErrorPropagate(localError, error);
         localError = NULL;
     }
-    CFReleaseNull(peerIds);
+    CFReleaseNull(ids);
     CFReleaseSafe(localError);
+    
     return result;
 }
 
+
+
 bool SOSAccountCleanupAfterPeer(SOSAccountRef account, size_t seconds, SOSCircleRef circle,
                                 SOSPeerInfoRef cleanupPeer, CFErrorRef* error)
 {
@@ -757,22 +1155,49 @@ bool SOSAccountScanForRetired(SOSAccountRef account, SOSCircleRef circle, CFErro
 
 SOSCircleRef SOSAccountCloneCircleWithRetirement(SOSAccountRef account, SOSCircleRef starting_circle, CFErrorRef *error) {
     SOSCircleRef new_circle = SOSCircleCopyCircle(NULL, starting_circle, error);
+    SOSFullPeerInfoRef meFull = SOSAccountGetMyFullPeerInfo(account);
+    SOSPeerInfoRef me = SOSFullPeerInfoGetPeerInfo(meFull);
+    bool iAmApplicant = me && SOSCircleHasApplicant(new_circle, me, NULL);
+    
     if(!new_circle) return NULL;
-
+    __block bool workDone = false;
     if (account->retirees) {
         CFSetForEach(account->retirees, ^(const void* value) {
             SOSPeerInfoRef pi = (SOSPeerInfoRef) value;
             if (isSOSPeerInfo(pi)) {
                 SOSCircleUpdatePeerInfo(new_circle, pi);
+                workDone = true;
             }
         });
     }
 
-    if(SOSCircleCountPeers(new_circle) == 0) {
-        SOSCircleResetToEmpty(new_circle, NULL);
-    }
-
-    return new_circle;
+    if(workDone && SOSCircleCountPeers(new_circle) == 0) {
+        SecKeyRef userPrivKey = SOSAccountGetPrivateCredential(account, error);
+        if(iAmApplicant) {
+            if(userPrivKey) {
+                secnotice("resetToOffering", "Reset to offering with last retirement and me as applicant");
+                if(!SOSCircleResetToOffering(new_circle, userPrivKey, meFull, error) ||
+                   !SOSAccountAddiCloudIdentity(account, new_circle, userPrivKey, error)) {
+                    CFReleaseNull(new_circle);
+                    return NULL;
+                }
+            } else {
+                // Do nothing.  We can't resetToOffering without a userPrivKey.  If we were to resetToEmpty
+                // we won't push the result later in handleUpdateCircle.  If we leave the circle as it is
+                // we have a chance to set things right with a SetCreds/Join sequence.  This will cause
+                // handleUpdateCircle to return false.
+                CFReleaseNull(new_circle);
+                return NULL;
+            }
+        } else {
+            // This case is when we aren't an applicant and the circle is retirement-empty.
+            secnotice("resetToEmpty", "Reset to empty with last retirement");
+            SOSCircleResetToEmpty(new_circle, NULL);
+        }
+    }
+
+    return new_circle;
 }
 
 //
@@ -993,10 +1418,21 @@ static SOSCCStatus SOSCCThisDeviceStatusInCircle(SOSCircleRef circle, SOSPeerInf
     return kSOSCCNotInCircle;
 }
 
+CFStringRef SOSAccountGetSOSCCStatusString(SOSCCStatus status) {
+    switch(status) {
+        case kSOSCCInCircle: return CFSTR("kSOSCCInCircle");
+        case kSOSCCNotInCircle: return CFSTR("kSOSCCNotInCircle");
+        case kSOSCCRequestPending: return CFSTR("kSOSCCRequestPending");
+        case kSOSCCCircleAbsent: return CFSTR("kSOSCCCircleAbsent");
+        case kSOSCCError: return CFSTR("kSOSCCError");
+    }
+    return CFSTR("kSOSCCError");
+}
+
 bool SOSAccountIsInCircle(SOSAccountRef account, CFErrorRef *error) {
     SOSCCStatus result = SOSAccountGetCircleStatus(account, error);
     
-    if (result != kSOSCCInCircle && result != kSOSCCError) {
+    if (result != kSOSCCInCircle) {
         SOSErrorCreate(kSOSErrorNoCircle, error, NULL, CFSTR("Not in circle"));
         return false;
     }
@@ -1016,14 +1452,54 @@ SOSCCStatus SOSAccountGetCircleStatus(SOSAccountRef account, CFErrorRef* error)
 // MARK: Account Reset Circles
 //
 
-static bool SOSAccountResetCircleToOffering(SOSAccountRef account, SecKeyRef user_key, CFErrorRef *error) {
+// This needs to be called within a SOSAccountModifyCircle() block
+
+bool SOSAccountAddiCloudIdentity(SOSAccountRef account, SOSCircleRef circle, SecKeyRef user_key, CFErrorRef *error) {
+    bool result = false;
+    SOSFullPeerInfoRef cloud_identity = NULL;
+    SOSPeerInfoRef cloud_peer = GenerateNewCloudIdentityPeerInfo(error);
+    require_quiet(cloud_peer, err_out);
+    cloud_identity = CopyCloudKeychainIdentity(cloud_peer, error);
+    CFReleaseNull(cloud_peer);
+    require_quiet(cloud_identity, err_out);
+    require_quiet(SOSCircleRequestAdmission(circle, user_key, cloud_identity, error), err_out);
+    require_quiet(SOSCircleAcceptRequest(circle, user_key, account->my_identity, SOSFullPeerInfoGetPeerInfo(cloud_identity), error), err_out);
+    result = true;
+err_out:
+    return result;
+}
+
+bool SOSAccountRemoveIncompleteiCloudIdentities(SOSAccountRef account, SOSCircleRef circle, SecKeyRef privKey, CFErrorRef *error) {
+    bool retval = false;
+    CFMutableSetRef iCloud2Remove = CFSetCreateMutableForCFTypes(kCFAllocatorDefault);
+    
+    SOSCircleForEachActivePeer(circle, ^(SOSPeerInfoRef peer) {
+        if(SOSPeerInfoIsCloudIdentity(peer)) {
+            SOSFullPeerInfoRef icfpi = SOSFullPeerInfoCreateCloudIdentity(kCFAllocatorDefault, peer, NULL);
+            if(!icfpi) {
+                CFSetAddValue(iCloud2Remove, peer);
+            }
+            CFReleaseNull(icfpi);
+        }
+    });
+    
+    if(CFSetGetCount(iCloud2Remove) > 0) {
+        retval = true;
+        SOSCircleRemovePeers(circle, privKey, account->my_identity, iCloud2Remove, error);
+    }
+    CFReleaseNull(iCloud2Remove);
+    return retval;
+}
+
+static bool SOSAccountResetCircleToOffering(SOSAccountTransactionRef aTxn, SecKeyRef user_key, CFErrorRef *error) {
+    SOSAccountRef account = aTxn->account;
     bool result = false;
 
     require(SOSAccountHasCircle(account, error), fail);
     require(SOSAccountEnsureFullPeerAvailable(account, error), fail);
     
     (void) SOSAccountResetAllRings(account, error);
-    
+
     SOSAccountModifyCircle(account, error, ^(SOSCircleRef circle) {
         bool result = false;
         SOSFullPeerInfoRef cloud_identity = NULL;
@@ -1031,18 +1507,10 @@ static bool SOSAccountResetCircleToOffering(SOSAccountRef account, SecKeyRef use
 
         require_quiet(SOSCircleResetToOffering(circle, user_key, account->my_identity, &localError), err_out);
 
-        {
-            SOSPeerInfoRef cloud_peer = GenerateNewCloudIdentityPeerInfo(error);
-            require_quiet(cloud_peer, err_out);
-            cloud_identity = CopyCloudKeychainIdentity(cloud_peer, error);
-            CFReleaseNull(cloud_peer);
-            require_quiet(cloud_identity, err_out);
-        }
-
         account->departure_code = kSOSNeverLeftCircle;
         require_quiet(SOSAccountAddEscrowToPeerInfo(account, SOSAccountGetMyFullPeerInfo(account), error), err_out);
-        require_quiet(SOSCircleRequestAdmission(circle, user_key, cloud_identity, &localError), err_out);
-        require_quiet(SOSCircleAcceptRequest(circle, user_key, account->my_identity, SOSFullPeerInfoGetPeerInfo(cloud_identity), &localError), err_out);
+
+        require_quiet(SOSAccountAddiCloudIdentity(account, circle, user_key, error), err_out);
         result = true;
         SOSAccountPublishCloudParameters(account, NULL);
 
@@ -1058,6 +1526,9 @@ static bool SOSAccountResetCircleToOffering(SOSAccountRef account, SecKeyRef use
         return result;
     });
 
+    SOSAccountSetValue(account, kSOSUnsyncedViewsKey, kCFBooleanTrue, NULL);
+    SOSAccountUpdateOutOfSyncViews(aTxn, SOSViewsGetAllCurrent());
+
     result = true;
 
 fail:
@@ -1065,14 +1536,16 @@ fail:
 }
 
 
-bool SOSAccountResetToOffering(SOSAccountRef account, CFErrorRef* error) {
+bool SOSAccountResetToOffering(SOSAccountTransactionRef aTxn, CFErrorRef* error) {
+    SOSAccountRef account = aTxn->account;
     SecKeyRef user_key = SOSAccountGetPrivateCredential(account, error);
     if (!user_key)
         return false;
 
     CFReleaseNull(account->my_identity);
+    secnotice("resetToOffering", "Resetting circle to offering by request from client");
 
-    return user_key && SOSAccountResetCircleToOffering(account, user_key, error);
+    return user_key && SOSAccountResetCircleToOffering(aTxn, user_key, error);
 }
 
 bool SOSAccountResetToEmpty(SOSAccountRef account, CFErrorRef* error) {
@@ -1085,6 +1558,7 @@ bool SOSAccountResetToEmpty(SOSAccountRef account, CFErrorRef* error) {
     CFReleaseNull(account->my_identity);
 
     account->departure_code = kSOSWithdrewMembership;
+    secnotice("resetToEmpty", "Reset Circle to empty by client request");
     result &= SOSAccountModifyCircle(account, error, ^(SOSCircleRef circle) {
         result = SOSCircleResetToEmpty(circle, error);
         return result;
@@ -1093,198 +1567,63 @@ bool SOSAccountResetToEmpty(SOSAccountRef account, CFErrorRef* error) {
     if (!result) {
         secerror("error: %@", error ? *error : NULL);
     }
-
     return result;
 }
-
-
 //
-// MARK: Waiting for in-sync
+// MARK: start backups
 //
 
-static bool SOSAccountHasBeenInSync(SOSAccountRef account) {
-    CFTypeRef unsyncedObject = SOSAccountGetValue(account, kSOSUnsyncedViewsKey, NULL);
-    CFSetRef unsynced = asSet(unsyncedObject, NULL);
+bool SOSAccountEnsureInBackupRings(SOSAccountRef account) {
+    __block bool result = false;
+    __block CFErrorRef error = NULL;
+    secnotice("backup", "Ensuring in rings");
 
-    return !(unsyncedObject == kCFBooleanTrue || (unsynced && (CFSetGetCount(unsynced) > 0)));
-}
+    CFDataRef backupKey = NULL;
 
-static bool SOSAccountUpdateOutOfSyncViews(SOSAccountRef account, CFSetRef viewsInSync) {
-    bool notifyOfChange = false;
+    require_action_quiet(account->backup_key, exit, result = true);
 
-    SOSCCStatus circleStatus = SOSAccountGetCircleStatus(account, NULL);
-    bool inOrApplying = (circleStatus == kSOSCCInCircle) || (circleStatus == kSOSCCRequestPending);
+    backupKey = SOSPeerInfoV2DictionaryCopyData(SOSAccountGetMyPeerInfo(account), sBackupKeyKey);
 
-    CFTypeRef unsyncedObject = SOSAccountGetValue(account, kSOSUnsyncedViewsKey, NULL);
+    require_action_quiet(!CFEqualSafe(backupKey, account->backup_key), exit, result = true); // If we're already set up, we're done.
+    require_quiet(SOSAccountUpdatePeerInfo(account, CFSTR("Backup public key"), &error, ^bool(SOSFullPeerInfoRef fpi, CFErrorRef *error) {
+                                               return SOSFullPeerInfoUpdateBackupKey(fpi, account->backup_key, error);
+                                           }), exit);
 
-    if (!inOrApplying) {
-        if (unsyncedObject != NULL) {
-            SOSAccountClearValue(account, kSOSUnsyncedViewsKey, NULL);
-            secnotice("initial-sync", "in sync, clearing pending");
-            notifyOfChange = true;
-        }
-    } else if (circleStatus == kSOSCCInCircle) {
-        __block CFMutableSetRef viewsToSync = CFSetCreateMutableForCFTypes(kCFAllocatorDefault);
-        SOSAccountForEachCirclePeerExceptMe(account, ^(SOSPeerInfoRef peer) {
-            SOSPeerInfoWithEnabledViewSet(peer, ^(CFSetRef enabled) {
-                CFSetUnion(viewsToSync, enabled);
-            });
-        });
+    require_quiet(account->backup_key, exit); // If it went null, we're done now.
 
-        if (viewsInSync) {
-            CFSetSubtract(viewsToSync, viewsInSync);
-
-        }
-
-        if (unsyncedObject == kCFBooleanTrue) {
-            if (CFSetGetCount(viewsToSync) == 0) {
-                secnotice("initial-sync", "No views to wait for");
-                SOSAccountClearValue(account, kSOSUnsyncedViewsKey, NULL);
-            } else {
-                __block CFSetRef newViews = NULL;
-                SOSPeerInfoWithEnabledViewSet(SOSAccountGetMyPeerInfo(account), ^(CFSetRef enabled) {
-                    newViews = CFSetCreateIntersection(kCFAllocatorDefault, enabled, viewsToSync);
-                });
-                secnotice("initial-sync", "Pending views set from True: %@", newViews);
-                SOSAccountSetValue(account, kSOSUnsyncedViewsKey, newViews, NULL);
-                CFReleaseNull(newViews);
-            }
-            notifyOfChange = true;
-        } else if (isSet(unsyncedObject)) {
-            CFSetRef waiting = (CFMutableSetRef) unsyncedObject;
-            CFSetRef newViews = CFSetCreateIntersection(kCFAllocatorDefault, waiting, viewsToSync);
-            if (!CFEqualSafe(waiting, newViews)) {
-                secnotice("initial-sync", "Pending views updated: %@", newViews);
-                SOSAccountSetValue(account, kSOSUnsyncedViewsKey, newViews, NULL);
-                notifyOfChange = true;
-            }
-            CFReleaseNull(newViews);
-        }
+    require_quiet(SOSBSKBIsGoodBackupPublic(account->backup_key, &error), exit);
 
-        CFReleaseNull(viewsToSync);
-    }
-
-    if (notifyOfChange) {
-        secnotice("initial-sync-notify", "In sync: Posting: %s", kSOSCCInitialSyncChangedNotification);
-        notify_post(kSOSCCInitialSyncChangedNotification);
-        // Make sure we update the engine
-        account->circle_rings_retirements_need_attention = true;
-    }
-
-    return SOSAccountHasBeenInSync(account);
-}
-
-static void SOSAccountPeerGotInSync(SOSAccountRef account, CFStringRef peerID) {
-    secnotice("initial-sync", "Heard PeerID is in sync: %@", peerID);
-
-    if (account->trusted_circle) {
-        SOSPeerInfoRef peer = SOSCircleCopyPeerWithID(account->trusted_circle, peerID, NULL);
-        if (peer) {
-            CFSetRef views = SOSPeerInfoCopyEnabledViews(peer);
-            SOSAccountUpdateOutOfSyncViews(account, views);
-            CFReleaseNull(views);
-        }
-        CFReleaseNull(peer);
+    // It's a good key, we're going with it. Stop backing up the old way.
+    CFErrorRef localError = NULL;
+    if (!SOSDeleteV0Keybag(&localError)) {
+        secerror("Failed to delete v0 keybag: %@", localError);
     }
-}
-
-void SOSAccountCleanupNotificationForAllPeers(SOSAccountRef account) {
-    SOSEngineRef engine = SOSDataSourceFactoryGetEngineForDataSourceName(account->factory, SOSCircleGetName(account->trusted_circle), NULL);
+    CFReleaseNull(localError);
+    
+    result = true;
 
-    CFDictionaryForEach(account->notification_cleanups, ^(const void *key, const void *value) {
-        if (engine) {
-            SOSEngineSetSyncCompleteListener(engine, key, NULL);
-        }
-        dispatch_async(account->queue, value);
+    // Setup backups the new way.
+    SOSAccountForEachBackupView(account, ^(const void *value) {
+        CFStringRef viewName = (CFStringRef)value;
+        result &= SOSAccountNewBKSBForView(account, viewName, &error);
     });
 
-    CFDictionaryRemoveAllValues(account->notification_cleanups);
-}
-
-static void SOSAccountCleanupNotificationForPeer(SOSAccountRef account, CFStringRef peerID) {
-    dispatch_block_t cleanup = CFDictionaryGetValue(account->notification_cleanups, peerID);
-
-    if (cleanup) {
-        SOSEngineRef engine = SOSDataSourceFactoryGetEngineForDataSourceName(account->factory, SOSCircleGetName(account->trusted_circle), NULL);
-
-        if (engine) {
-            SOSEngineSetSyncCompleteListener(engine, peerID, NULL);
-        }
-
-        dispatch_async(account->queue, cleanup);
-    }
-
-    CFDictionaryRemoveValue(account->notification_cleanups, peerID);
-
-}
-
-static void SOSAccountRegisterCleanupBlock(SOSAccountRef account, CFStringRef peerID, dispatch_block_t block) {
-    dispatch_block_t copy = Block_copy(block);
-    CFDictionarySetValue(account->notification_cleanups, peerID, copy);
-    CFReleaseNull(copy);
-}
-
-void SOSAccountEnsureSyncChecking(SOSAccountRef account) {
-    if (CFDictionaryGetCount(account->notification_cleanups) == 0) {
-        secnotice("initial-sync", "Setting up notifications to monitor in-sync");
-        SOSEngineRef engine = SOSDataSourceFactoryGetEngineForDataSourceName(account->factory, SOSCircleGetName(account->trusted_circle), NULL);
-
-        SOSEngineSetSyncCompleteListenerQueue(engine, account->queue);
-
-        if (engine) {
-            SOSAccountForEachCirclePeerExceptMe(account, ^(SOSPeerInfoRef peer) {
-                CFStringRef peerID = CFStringCreateCopy(kCFAllocatorDefault, SOSPeerInfoGetPeerID(peer));
-
-                secnotice("initial-sync", "Setting up monitoring for peer: %@", peerID);
-                SOSAccountRegisterCleanupBlock(account, peerID, ^{
-                    CFReleaseSafe(peerID);
-                });
-
-                SOSEngineSetSyncCompleteListener(engine, peerID, ^{
-                    SOSAccountPeerGotInSync(account, peerID);
-                    SOSAccountCleanupNotificationForPeer(account, peerID);
-                    SOSAccountFinishTransaction(account);
-                });
-            });
-        } else {
-            secerror("Couldn't find engine to setup notifications!!!");
-        }
-    }
-}
-
-void SOSAccountCancelSyncChecking(SOSAccountRef account) {
-    SOSAccountCleanupNotificationForAllPeers(account);
-    SOSAccountUpdateOutOfSyncViews(account, NULL);
-}
-
-bool SOSAccountCheckHasBeenInSync(SOSAccountRef account) {
-    bool hasBeenInSync = false;
-
-    if (!SOSAccountIsInCircle(account, NULL)) {
-        SOSAccountCancelSyncChecking(account);
-    } else {
-        hasBeenInSync = SOSAccountHasBeenInSync(account);
-        if (!hasBeenInSync) {
-            hasBeenInSync = SOSAccountUpdateOutOfSyncViews(account, NULL);
-            if (hasBeenInSync) {
-                // Cancel and declare victory
-                SOSAccountCancelSyncChecking(account);
-            } else {
-                // Make sure we're watching in case this is the fist attempt
-                SOSAccountEnsureSyncChecking(account);
-            }
-        }
+exit:
+    if (!result) {
+        secnotice("backupkey", "Failed to setup backup public key: %@", error ? (CFTypeRef) error : (CFTypeRef) CFSTR("No error space provided"));
     }
-
-    return hasBeenInSync;
+    CFReleaseNull(backupKey);
+    return result;
 }
 
 //
 // MARK: Joining
 //
 
-static bool SOSAccountJoinCircle(SOSAccountRef account, SecKeyRef user_key,
+static bool SOSAccountJoinCircle(SOSAccountTransactionRef aTxn, SecKeyRef user_key,
                                 bool use_cloud_peer, CFErrorRef* error) {
+    SOSAccountRef account = aTxn->account;
+
     __block bool result = false;
     __block SOSFullPeerInfoRef cloud_full_peer = NULL;
 
@@ -1293,15 +1632,17 @@ static bool SOSAccountJoinCircle(SOSAccountRef account, SecKeyRef user_key,
 
     SOSFullPeerInfoRef myCirclePeer = account->my_identity;
 
-    if (use_cloud_peer) {
-        cloud_full_peer = SOSCircleCopyiCloudFullPeerInfoRef(account->trusted_circle, NULL);
+    if (SOSCircleCountPeers(account->trusted_circle) == 0) {
+        secnotice("resetToOffering", "Resetting circle to offering since there are no peers");
+        // this also clears initial sync data
+        result = SOSAccountResetCircleToOffering(aTxn, user_key, error);
     } else {
         SOSAccountSetValue(account, kSOSUnsyncedViewsKey, kCFBooleanTrue, NULL);
-    }
 
-    if (SOSCircleCountPeers(account->trusted_circle) == 0) {
-        result = SOSAccountResetCircleToOffering(account, user_key, error);
-    } else {
+        if (use_cloud_peer) {
+            cloud_full_peer = SOSCircleCopyiCloudFullPeerInfoRef(account->trusted_circle, NULL);
+        }
+
         SOSAccountModifyCircle(account, error, ^(SOSCircleRef circle) {
             result = SOSAccountAddEscrowToPeerInfo(account, myCirclePeer, error);
             result &= SOSCircleRequestAdmission(circle, user_key, myCirclePeer, error);
@@ -1312,6 +1653,7 @@ static bool SOSAccountJoinCircle(SOSAccountRef account, SecKeyRef user_key,
                 require_quiet(cloudid, finish);
                 require_quiet(SOSCircleHasActivePeerWithID(circle, cloudid, &localError), finish);
                 require_quiet(SOSCircleAcceptRequest(circle, user_key, cloud_full_peer, SOSFullPeerInfoGetPeerInfo(myCirclePeer), &localError), finish);
+
             finish:
                 if (localError){
                     secerror("Failed to join with cloud identity: %@", localError);
@@ -1320,6 +1662,10 @@ static bool SOSAccountJoinCircle(SOSAccountRef account, SecKeyRef user_key,
             }
             return result;
         });
+
+        if (use_cloud_peer) {
+            SOSAccountUpdateOutOfSyncViews(aTxn, SOSViewsGetAllCurrent());
+        }
     }
 
 fail:
@@ -1327,7 +1673,8 @@ fail:
     return result;
 }
 
-static bool SOSAccountJoinCircles_internal(SOSAccountRef account, bool use_cloud_identity, CFErrorRef* error) {
+static bool SOSAccountJoinCircles_internal(SOSAccountTransactionRef aTxn, bool use_cloud_identity, CFErrorRef* error) {
+    SOSAccountRef account = aTxn->account;
     bool success = false;
 
     SecKeyRef user_key = SOSAccountGetPrivateCredential(account, error);
@@ -1350,7 +1697,7 @@ static bool SOSAccountJoinCircles_internal(SOSAccountRef account, bool use_cloud
         }
     }
 
-    success = SOSAccountJoinCircle(account, user_key, use_cloud_identity, error);
+    success = SOSAccountJoinCircle(aTxn, user_key, use_cloud_identity, error);
 
     require_quiet(success, done);
        
@@ -1360,8 +1707,8 @@ done:
     return success;
 }
 
-bool SOSAccountJoinCircles(SOSAccountRef account, CFErrorRef* error) {
-    return SOSAccountJoinCircles_internal(account, false, error);
+bool SOSAccountJoinCircles(SOSAccountTransactionRef aTxn, CFErrorRef* error) {
+    return SOSAccountJoinCircles_internal(aTxn, false, error);
 }
 
 CFStringRef SOSAccountCopyDeviceID(SOSAccountRef account, CFErrorRef *error){
@@ -1377,78 +1724,58 @@ fail:
 
 bool SOSAccountSetMyDSID(SOSAccountRef account, CFStringRef IDS, CFErrorRef* error){
     bool result = true;
-
-    if(whichTransportType == kSOSTransportIDS || whichTransportType == kSOSTransportFuture){
-        secdebug("IDS Transport", "We are setting our device ID: %@", IDS);
-        if(IDS != NULL && (CFStringGetLength(IDS) > 0)){
-            require_action_quiet(account->my_identity, fail, SOSErrorCreate(kSOSErrorPeerNotFound, error, NULL, CFSTR("No peer for me")));
+    
+    secdebug("IDS Transport", "We are setting our device ID: %@", IDS);
+    if(IDS != NULL && (CFStringGetLength(IDS) > 0)){
+        require_action_quiet(account->my_identity, fail, SOSErrorCreate(kSOSErrorPeerNotFound, error, NULL, CFSTR("No peer for me")));
+        
+        result = SOSAccountModifyCircle(account, error, ^bool(SOSCircleRef circle) {
             
-            result = SOSAccountModifyCircle(account, error, ^bool(SOSCircleRef circle) {
-                
-                SOSFullPeerInfoUpdateDeviceID(account->my_identity, IDS, error);
-                SOSFullPeerInfoUpdateTransportType(account->my_identity, SOSTransportMessageTypeIDS, error);
-                SOSFullPeerInfoUpdateTransportPreference(account->my_identity, kCFBooleanTrue, error);
-                
-                return SOSCircleHasPeer(circle, SOSFullPeerInfoGetPeerInfo(account->my_identity), NULL);
-            });
-        }
-        else
-            result = false;
-    }
-    else{
-        secdebug("IDS Transport", "We are setting our device ID: %@", IDS);
-        if(IDS != NULL && (CFStringGetLength(IDS) > 0)){
-            require_action_quiet(account->my_identity, fail, SOSErrorCreate(kSOSErrorPeerNotFound, error, NULL, CFSTR("No peer for me")));
+            SOSFullPeerInfoUpdateDeviceID(account->my_identity, IDS, error);
+            SOSFullPeerInfoUpdateTransportType(account->my_identity, SOSTransportMessageTypeIDSV2, error);
+            SOSFullPeerInfoUpdateTransportPreference(account->my_identity, kCFBooleanFalse, error);
+            SOSFullPeerInfoUpdateTransportFragmentationPreference(account->my_identity, kCFBooleanTrue, error);
             
-            result = SOSAccountModifyCircle(account, error, ^bool(SOSCircleRef circle) {
-                
-                SOSFullPeerInfoUpdateDeviceID(account->my_identity, IDS, error);
-                SOSFullPeerInfoUpdateTransportType(account->my_identity, SOSTransportMessageTypeKVS, error);
-                SOSFullPeerInfoUpdateTransportPreference(account->my_identity, kCFBooleanTrue, error);
-                
-                return SOSCircleHasPeer(circle, SOSFullPeerInfoGetPeerInfo(account->my_identity), NULL);
-            });
-        }
-        else
-            result = false;
-
+            return SOSCircleHasPeer(circle, SOSFullPeerInfoGetPeerInfo(account->my_identity), NULL);
+        });
     }
-   
+    else
+        result = false;
+    
     SOSCCSyncWithAllPeers();
     
 fail:
+    CFReleaseNull(account->deviceID);
+    account->deviceID = CFRetainSafe(IDS);
     return result;
 }
 
-
 bool SOSAccountSendIDSTestMessage(SOSAccountRef account, CFStringRef message, CFErrorRef *error){
     bool result = true;
-    if(whichTransportType == kSOSTransportIDS || whichTransportType == kSOSTransportFuture || whichTransportType == kSOSTransportPresent){
-        //construct message dictionary, circle -> peerID -> message
-        
-        CFMutableDictionaryRef circleToPeerMessages = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
-        CFMutableDictionaryRef peerToMessage = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
-        
-        char *messageCharStar;
-        asprintf(&messageCharStar, "%d", kIDSSendOneMessage);
-        CFStringRef messageString = CFStringCreateWithCString(kCFAllocatorDefault, messageCharStar, kCFStringEncodingUTF8);
-        
-        CFMutableDictionaryRef mutableDictionary = CFDictionaryCreateMutableForCFTypesWith(kCFAllocatorDefault, messageString, CFSTR("send IDS test message"), NULL);
-        
-        SOSCircleForEachPeer(account->trusted_circle, ^(SOSPeerInfoRef peer) {
-            if(!CFEqualSafe(peer, SOSAccountGetMyPeerInfo(account)))
+    //construct message dictionary, circle -> peerID -> message
+    
+    CFMutableDictionaryRef circleToPeerMessages = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
+    CFMutableDictionaryRef peerToMessage = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
+    
+    char *messageCharStar;
+    asprintf(&messageCharStar, "%d", kIDSSendOneMessage);
+    CFStringRef messageString = CFStringCreateWithCString(kCFAllocatorDefault, messageCharStar, kCFStringEncodingUTF8);
+    
+    CFMutableDictionaryRef mutableDictionary = CFDictionaryCreateMutableForCFTypesWith(kCFAllocatorDefault, kIDSOperationType, messageString, kIDSMessageToSendKey, CFSTR("send IDS test message"), NULL);
+    
+    SOSCircleForEachPeer(account->trusted_circle, ^(SOSPeerInfoRef peer) {
+        if(!CFEqualSafe(peer, SOSAccountGetMyPeerInfo(account)))
             CFDictionaryAddValue(peerToMessage, SOSPeerInfoGetPeerID(peer), mutableDictionary);
-        });
-        
-        CFDictionaryAddValue(circleToPeerMessages, SOSCircleGetName(account->trusted_circle), peerToMessage);
-        result = SOSTransportMessageSendMessages(account->ids_message_transport, circleToPeerMessages, error);
-        
-        CFReleaseNull(mutableDictionary);
-        CFReleaseNull(peerToMessage);
-        CFReleaseNull(circleToPeerMessages);
-        CFReleaseNull(messageString);
-        free(messageCharStar);
-    }
+    });
+    
+    CFDictionaryAddValue(circleToPeerMessages, SOSCircleGetName(account->trusted_circle), peerToMessage);
+    result = SOSTransportMessageSendMessages(account->ids_message_transport, circleToPeerMessages, error);
+    
+    CFReleaseNull(mutableDictionary);
+    CFReleaseNull(peerToMessage);
+    CFReleaseNull(circleToPeerMessages);
+    CFReleaseNull(messageString);
+    free(messageCharStar);
     return result;
 }
 
@@ -1465,9 +1792,11 @@ bool SOSAccountStartPingTest(SOSAccountRef account, CFStringRef message, CFError
     
     char *messageCharStar;
     asprintf(&messageCharStar, "%d", kIDSStartPingTestMessage);
-    CFStringRef messageString = CFStringCreateWithCString(kCFAllocatorDefault, messageCharStar, kCFStringEncodingUTF8);
+    CFStringRef operationToString = CFStringCreateWithCString(kCFAllocatorDefault, messageCharStar, kCFStringEncodingUTF8);
+    CFStringRef messageToSend = CFSTR("send IDS test message");
     
-    CFMutableDictionaryRef mutableDictionary = CFDictionaryCreateMutableForCFTypesWith(kCFAllocatorDefault, messageString, CFSTR("send IDS test message"), NULL);
+    CFMutableDictionaryRef mutableDictionary = CFDictionaryCreateMutableForCFTypesWith(kCFAllocatorDefault, kIDSOperationType, operationToString, kIDSMessageToSendKey, messageToSend, NULL);
+
     
     SOSCircleForEachPeer(account->trusted_circle, ^(SOSPeerInfoRef peer) {
         if(CFStringCompare(SOSAccountGetMyPeerID(account), SOSPeerInfoGetPeerID(peer), 0) != 0)
@@ -1480,7 +1809,7 @@ bool SOSAccountStartPingTest(SOSAccountRef account, CFStringRef message, CFError
     CFReleaseNull(mutableDictionary);
     CFReleaseNull(peerToMessage);
     CFReleaseNull(circleToPeerMessages);
-    CFReleaseNull(messageString);
+    CFReleaseNull(operationToString);
     free(messageCharStar);
 fail:
     return result;
@@ -1488,39 +1817,38 @@ fail:
 
 bool SOSAccountRetrieveDeviceIDFromIDSKeychainSyncingProxy(SOSAccountRef account, CFErrorRef *error){
     bool result = true;
-    if(whichTransportType == kSOSTransportIDS || whichTransportType == kSOSTransportFuture || whichTransportType == kSOSTransportPresent){
-        
-        __block bool success = true;
-        __block CFErrorRef localError = NULL;
-        dispatch_semaphore_t wait_for = dispatch_semaphore_create(0);
-        dispatch_retain(wait_for); // Both this scope and the block own it
-        
-        SOSCloudKeychainGetIDSDeviceID(^(CFDictionaryRef returnedValues, CFErrorRef sync_error){
-            success = (sync_error == NULL);
-            if (!success) {
-                CFRetainAssign(localError, sync_error);
-            }
-            
-            dispatch_semaphore_signal(wait_for);
-            dispatch_release(wait_for);
-        });
+    
+    __block bool success = true;
+    __block CFErrorRef localError = NULL;
+    dispatch_semaphore_t wait_for = dispatch_semaphore_create(0);
+    dispatch_retain(wait_for); // Both this scope and the block own it
+    
+    SOSCloudKeychainGetIDSDeviceID(^(CFDictionaryRef returnedValues, CFErrorRef sync_error){
+        success = (sync_error == NULL);
+        if (!success) {
+            CFRetainAssign(localError, sync_error);
+        }
         
-        dispatch_semaphore_wait(wait_for, DISPATCH_TIME_FOREVER);
+        dispatch_semaphore_signal(wait_for);
         dispatch_release(wait_for);
-        
-        if(!success && localError != NULL && error != NULL){
-            secerror("Could not ask IDSKeychainSyncingProxy for Device ID: %@", localError);
-            *error = localError;
-        }
-        else{
-            secdebug("IDS Transport", "Attempting to retrieve the IDS Device ID");
-        }
+    });
+    
+    dispatch_semaphore_wait(wait_for, DISPATCH_TIME_FOREVER);
+    dispatch_release(wait_for);
+    
+    if(!success && localError != NULL && error != NULL){
+        secerror("Could not ask IDSKeychainSyncingProxy for Device ID: %@", localError);
+        *error = localError;
+        result = false;
+    }
+    else{
+        secdebug("IDS Transport", "Attempting to retrieve the IDS Device ID");
     }
     return result;
 }
 
-bool SOSAccountJoinCirclesAfterRestore(SOSAccountRef account, CFErrorRef* error) {
-    return SOSAccountJoinCircles_internal(account, true, error);
+bool SOSAccountJoinCirclesAfterRestore(SOSAccountTransactionRef aTxn, CFErrorRef* error) {
+    return SOSAccountJoinCircles_internal(aTxn, true, error);
 }
 
 
@@ -1528,6 +1856,7 @@ bool SOSAccountLeaveCircle(SOSAccountRef account, CFErrorRef* error)
 {
     bool result = true;
 
+    secnotice("leaveCircle", "Leaving circle by client request");
     result &= SOSAccountModifyCircle(account, error, ^(SOSCircleRef circle) {
         return sosAccountLeaveCircle(account, circle, error);
     });
@@ -1554,12 +1883,14 @@ bool SOSAccountRemovePeersFromCircle(SOSAccountRef account, CFArrayRef peers, CF
     result &= SOSAccountModifyCircle(account, error, ^(SOSCircleRef circle) {
         bool success = false;
 
-        require_quiet(SOSCircleRemovePeers(circle, user_key, SOSAccountGetMyFullPeerInfo(account), peersToRemove, error), done);
+        if(CFSetGetCount(peersToRemove) != 0) {
+            require_quiet(SOSCircleRemovePeers(circle, user_key, SOSAccountGetMyFullPeerInfo(account), peersToRemove, error), done);
+            success = SOSAccountGenerationSignatureUpdate(account, error);
+        } else success = true;
 
-        if (leaveCircle) {
+        if (success && leaveCircle) {
+            secnotice("leaveCircle", "Leaving circle by client request");
             success = sosAccountLeaveCircle(account, circle, error);
-        } else {
-            success = SOSAccountGenerationSignatureUpdate(account, error);
         }
 
     done:
@@ -1579,6 +1910,7 @@ bool SOSAccountBail(SOSAccountRef account, uint64_t limit_in_seconds, CFErrorRef
     // Add a task to the group
     dispatch_group_async(group, queue, ^{
         SOSAccountModifyCircle(account, error, ^(SOSCircleRef circle) {
+            secnotice("leaveCircle", "Leaving circle by client request");
             return sosAccountLeaveCircle(account, circle, error);
         });
     });
@@ -1699,28 +2031,26 @@ bool SOSAccountEnsurePeerRegistration(SOSAccountRef account, CFErrorRef *error)
 
     require_quiet(account->trusted_circle, done);
     require_quiet(account->my_identity, done);
+    require_quiet(account->user_public_trusted, done);
+    
     // If we are not in the circle, there is no point in setting up peers
     require_quiet(SOSAccountIsMyPeerActive(account, NULL), done);
 
     // This code only uses the SOSFullPeerInfoRef for two things:
     //  - Finding out if this device is in the trusted circle
     //  - Using the peerID for this device to see if the current peer is "me"
-    //  - It is used indirectly by passing account->my_identity to SOSPeerCoderInitializeForPeer
+    //  - It is used indirectly by passing account->my_identity to SOSEngineInitializePeerCoder
     
     CFStringRef my_id = SOSPeerInfoGetPeerID(SOSFullPeerInfoGetPeerInfo(account->my_identity));
 
-    SOSCircleForEachPeer(account->trusted_circle, ^(SOSPeerInfoRef peer) {
+    SOSCircleForEachValidSyncingPeer(account->trusted_circle, account->user_public, ^(SOSPeerInfoRef peer) {
         if (!SOSPeerInfoPeerIDEqual(peer, my_id)) {
             CFErrorRef localError = NULL;
             SOSTransportMessageRef messageTransport = NULL;
             
-            if(whichTransportType == kSOSTransportIDS || whichTransportType == kSOSTransportFuture || whichTransportType == kSOSTransportPresent){
-                 messageTransport = SOSPeerInfoHasDeviceID(peer) ? account->ids_message_transport : account->kvs_message_transport;
-            }
-            else
-                messageTransport = account->kvs_message_transport;
+            messageTransport = SOSPeerInfoHasDeviceID(peer) ? account->ids_message_transport : account->kvs_message_transport;
             
-            SOSPeerCoderInitializeForPeer(messageTransport->engine, account->my_identity, peer, &localError);
+            SOSEngineInitializePeerCoder(messageTransport->engine, account->my_identity, peer, &localError);
             if (localError)
                 secnotice("updates", "can't initialize transport for peer %@ with %@ (%@)", peer, account->my_identity, localError);
             CFReleaseSafe(localError);
@@ -1728,31 +2058,8 @@ bool SOSAccountEnsurePeerRegistration(SOSAccountRef account, CFErrorRef *error)
     });
 
     //Initialize our device ID
-    if(whichTransportType == kSOSTransportIDS || whichTransportType == kSOSTransportFuture || whichTransportType == kSOSTransportPresent){
-        CFStringRef deviceID = SOSPeerInfoCopyDeviceID(SOSFullPeerInfoGetPeerInfo(account->my_identity));
-        if( deviceID == NULL || CFStringGetLength(deviceID) == 0){
-            
-            __block bool success = true;
-            __block CFErrorRef localError = NULL;
-            
-                SOSCloudKeychainGetIDSDeviceID(^(CFDictionaryRef returnedValues, CFErrorRef sync_error){
-                    success = (sync_error == NULL);
-                    if (!success) {
-                        CFRetainAssign(localError, sync_error);
-                    }
-                });
-                
-                if(!success && localError != NULL && error != NULL){
-                    secerror("Could not ask IDSKeychainSyncingProxy for Device ID: %@", localError);
-                    *error = localError;
-                }
-                else{
-                    secdebug("IDS Transport", "Attempting to retrieve the IDS Device ID");
-                }
-            CFReleaseNull(localError);
-        }
-        CFReleaseNull(deviceID);
-    }
+    SOSTransportMessageIDSGetIDSDeviceID(account);    
+    
     
 done:
     return result;
@@ -1766,7 +2073,11 @@ static inline bool SOSAccountEnsureExpansion(SOSAccountRef account, CFErrorRef *
     return SecAllocationError(account->expansion, error, CFSTR("Can't Alloc Account Expansion dictionary"));
 }
 
-bool SOSAccountClearValue(SOSAccountRef account, const void *key, CFErrorRef *error) {
+//
+// Value manipulation
+//
+
+bool SOSAccountClearValue(SOSAccountRef account, CFStringRef key, CFErrorRef *error) {
     bool success = SOSAccountEnsureExpansion(account, error);
     require_quiet(success, errOut);
 
@@ -1775,7 +2086,9 @@ errOut:
     return success;
 }
 
-bool SOSAccountSetValue(SOSAccountRef account, const void *key, const void *value, CFErrorRef *error) {
+bool SOSAccountSetValue(SOSAccountRef account, CFStringRef key, CFTypeRef value, CFErrorRef *error) {
+    if (value == NULL) return SOSAccountClearValue(account, key, error);
+
     bool success = SOSAccountEnsureExpansion(account, error);
     require_quiet(success, errOut);
 
@@ -1784,8 +2097,7 @@ errOut:
     return success;
 }
 
-
-const void *SOSAccountGetValue(SOSAccountRef account, const void *key, CFErrorRef *error) {
+CFTypeRef SOSAccountGetValue(SOSAccountRef account, CFStringRef key, CFErrorRef *error) {
     if (!account->expansion) {
         return NULL;
     }
@@ -1826,7 +2138,7 @@ bool SOSAccountAddEscrowToPeerInfo(SOSAccountRef account, SOSFullPeerInfoRef myP
 bool SOSAccountCheckPeerAvailability(SOSAccountRef account, CFErrorRef *error)
 {
     CFMutableDictionaryRef circleToPeerMessages = NULL;
-    CFStringRef messageString = NULL;
+    CFStringRef operationTypeAsString = NULL;
     CFMutableDictionaryRef mutableDictionary = NULL;
     CFMutableSetRef peers = NULL;
     CFMutableDictionaryRef peerList = NULL;
@@ -1839,11 +2151,11 @@ bool SOSAccountCheckPeerAvailability(SOSAccountRef account, CFErrorRef *error)
     circleToPeerMessages = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
     
     //adding message type kIDSPeerAvailability so IDSKeychainSyncingProxy does not send this message as a keychain item
-  
+    
     asprintf(&message, "%d", kIDSPeerAvailability);
-    messageString = CFStringCreateWithCString(kCFAllocatorDefault, message, kCFStringEncodingUTF8);
+    operationTypeAsString = CFStringCreateWithCString(kCFAllocatorDefault, message, kCFStringEncodingUTF8);
     
-    mutableDictionary = CFDictionaryCreateMutableForCFTypesWith(kCFAllocatorDefault, messageString, CFSTR("checking peers"), NULL);
+    mutableDictionary = CFDictionaryCreateMutableForCFTypesWith(kCFAllocatorDefault, kIDSOperationType, operationTypeAsString, kIDSMessageToSendKey, CFSTR("checking peers"), NULL);
     
     //make sure there are peers in the circle
     peers = SOSCircleCopyPeers(account->trusted_circle, kCFAllocatorDefault);
@@ -1869,14 +2181,14 @@ bool SOSAccountCheckPeerAvailability(SOSAccountRef account, CFErrorRef *error)
             CFReleaseNull(intersectSets);
         }
     });
-    
+        
     require_quiet(CFDictionaryGetCount(peerList) > 0 , fail);
     CFDictionaryAddValue(circleToPeerMessages, SOSCircleGetName(account->trusted_circle), peerList);
     result = SOSTransportMessageSendMessages(account->ids_message_transport, circleToPeerMessages, error);
-   
+    
 fail:
     CFReleaseNull(mutableDictionary);
-    CFReleaseNull(messageString);
+    CFReleaseNull(operationTypeAsString);
     CFReleaseNull(peerList);
     CFReleaseNull(circleToPeerMessages);
     CFReleaseNull(peers);
@@ -1885,7 +2197,7 @@ fail:
 }
 
 
-static void SOSAccountRecordRetiredPeersInCircle(SOSAccountRef account) {
+void SOSAccountRecordRetiredPeersInCircle(SOSAccountRef account) {
     if (!SOSAccountIsInCircle(account, NULL))
         return;
 
@@ -1907,21 +2219,231 @@ static void SOSAccountRecordRetiredPeersInCircle(SOSAccountRef account) {
     });
 }
 
-void SOSAccountFinishTransaction(SOSAccountRef account) {
-    if(account->circle_rings_retirements_need_attention){
-        SOSAccountRecordRetiredPeersInCircle(account);
 
-        CFErrorRef localError = NULL;
-        if(!SOSTransportCircleFlushChanges(account->circle_transport, &localError)) {
-            secerror("flush circle failed %@", localError);
-        }
-        CFReleaseSafe(localError);
+static size_t SOSPiggyBackBlobGetDEREncodedSize(SOSGenCountRef gencount, SecKeyRef pubKey, CFDataRef signature, CFErrorRef *error) {
+    size_t total_payload = 0;
+    
+    CFDataRef publicBytes = NULL;
+    OSStatus result = SecKeyCopyPublicBytes(pubKey, &publicBytes);
+    
+    if (result != errSecSuccess) {
+        SOSCreateError(kSOSErrorBadKey, CFSTR("Failed to export public bytes"), NULL, error);
+        return 0;
+    }
+
+    require_quiet(accumulate_size(&total_payload, der_sizeof_number(gencount, error)), errOut);
+    require_quiet(accumulate_size(&total_payload, der_sizeof_data_or_null(publicBytes, error)), errOut);
+    require_quiet(accumulate_size(&total_payload, der_sizeof_data_or_null(signature, error)), errOut);
+    return ccder_sizeof(CCDER_CONSTRUCTED_SEQUENCE, total_payload);
+    
+errOut:
+    SecCFDERCreateError(kSecDERErrorUnknownEncoding, CFSTR("don't know how to encode"), NULL, error);
+    return 0;
+}
+
+static uint8_t* SOSPiggyBackBlobEncodeToDER(SOSGenCountRef gencount, SecKeyRef pubKey, CFDataRef signature, CFErrorRef* error, const uint8_t* der, uint8_t* der_end) {
+    CFDataRef publicBytes = NULL;
+
+    OSStatus result = SecKeyCopyPublicBytes(pubKey, &publicBytes);
+    
+    if (result != errSecSuccess) {
+        SOSCreateError(kSOSErrorBadKey, CFSTR("Failed to export public bytes"), NULL, error);
+        return NULL;
+    }
+
+    
+    der_end =  ccder_encode_constructed_tl(CCDER_CONSTRUCTED_SEQUENCE, der_end, der,
+            der_encode_number(gencount, error, der,
+            der_encode_data_or_null(publicBytes, error, der,
+            der_encode_data_or_null(signature, error, der, der_end))));
+    return der_end;
+}
+
+static CFDataRef SOSPiggyBackBlobCopyEncodedData(SOSGenCountRef gencount, SecKeyRef pubKey, CFDataRef signature, CFAllocatorRef allocator, CFErrorRef *error)
+{
+    return CFDataCreateWithDER(kCFAllocatorDefault, SOSPiggyBackBlobGetDEREncodedSize(gencount, pubKey, signature, error), ^uint8_t*(size_t size, uint8_t *buffer) {
+        return SOSPiggyBackBlobEncodeToDER(gencount, pubKey, signature, error, buffer, (uint8_t *) buffer + size);
+    });
+}
+
+struct piggyBackBlob {
+    SOSGenCountRef gencount;
+    SecKeyRef pubKey;
+    CFDataRef signature;
+};
+
+static struct piggyBackBlob *SOSPiggyBackBlobCreateFromDER(CFAllocatorRef allocator, CFErrorRef *error,
+                                                           const uint8_t** der_p, const uint8_t *der_end) {
+    const uint8_t *sequence_end;
+    struct piggyBackBlob *retval = NULL;
+    SOSGenCountRef gencount = NULL;
+    CFDataRef signature = NULL;
+    CFDataRef publicBytes = NULL;
+    
+    *der_p = ccder_decode_constructed_tl(CCDER_CONSTRUCTED_SEQUENCE, &sequence_end, *der_p, der_end);
+    require_action_quiet(sequence_end != NULL, errOut,
+                         SOSCreateError(kSOSErrorBadFormat, CFSTR("Bad Blob DER"), (error != NULL) ? *error : NULL, error));
+    *der_p = der_decode_number(allocator, 0, &gencount, error, *der_p, sequence_end);
+    *der_p = der_decode_data_or_null(kCFAllocatorDefault, &publicBytes, error, *der_p, der_end);
+    *der_p = der_decode_data_or_null(kCFAllocatorDefault, &signature, error, *der_p, der_end);
+    require_action_quiet(*der_p && *der_p == der_end, errOut,
+                         SOSCreateError(kSOSErrorBadFormat, CFSTR("Didn't consume all bytes for pbblob"), (error != NULL) ? *error : NULL, error));
+    retval = malloc(sizeof(struct piggyBackBlob));
+    retval->gencount = gencount;
+    retval->signature = signature;
+    retval->pubKey = SecKeyCreateFromPublicData(kCFAllocatorDefault, kSecECDSAAlgorithmID, publicBytes);
+    
+errOut:
+    if(!retval) {
+        CFReleaseNull(gencount);
+        CFReleaseNull(publicBytes);
+        CFReleaseNull(signature);
+    }
+    return retval;
+}
+
+static struct piggyBackBlob *SOSPiggyBackBlobCreateFromData(CFAllocatorRef allocator, CFDataRef blobData, CFErrorRef *error)
+{
+    size_t size = CFDataGetLength(blobData);
+    const uint8_t *der = CFDataGetBytePtr(blobData);
+    struct piggyBackBlob *inflated = SOSPiggyBackBlobCreateFromDER(allocator, error, &der, der + size);
+    return inflated;
+}
 
-        SOSAccountNotifyEngines(account); // For now our only rings are backup rings.
+
+
+SOSPeerInfoRef SOSAccountCopyApplication(SOSAccountRef account, CFErrorRef* error) {
+    SOSPeerInfoRef applicant = NULL;
+    SecKeyRef userKey = SOSAccountGetPrivateCredential(account, error);
+    if(!userKey) return false;
+    require_quiet(SOSAccountEnsureFullPeerAvailable(account, error), errOut);
+    require(SOSFullPeerInfoPromoteToApplication(account->my_identity, userKey, error), errOut);
+    applicant = SOSPeerInfoCreateCopy(kCFAllocatorDefault,  (SOSFullPeerInfoGetPeerInfo(account->my_identity)), error);
+errOut:
+    return applicant;
+}
+
+
+CFDataRef SOSAccountCopyCircleJoiningBlob(SOSAccountRef account, SOSPeerInfoRef applicant, CFErrorRef *error) {
+    SOSGenCountRef gencount = NULL;
+    CFDataRef signature = NULL;
+    SecKeyRef ourKey = NULL;
+
+    CFDataRef pbblob = NULL;
+
+    SecKeyRef userKey = SOSAccountGetTrustedPublicCredential(account, error);
+    require_quiet(userKey, errOut);
+
+    require_action_quiet(applicant, errOut, SOSCreateError(kSOSErrorProcessingFailure, CFSTR("No applicant provided"), (error != NULL) ? *error : NULL, error));
+    require_quiet(SOSPeerInfoApplicationVerify(applicant, userKey, error), errOut);
+
+    {
+        SOSFullPeerInfoRef fpi = SOSAccountGetMyFullPeerInfo(account);
+        ourKey = SOSFullPeerInfoCopyDeviceKey(fpi, error);
+        require_quiet(ourKey, errOut);
     }
 
-    SOSAccountCheckHasBeenInSync(account);
+    SOSCircleRef currentCircle = SOSAccountGetCircle(account, error);
+    require_quiet(currentCircle, errOut);
+
+    SOSCircleRef prunedCircle = SOSCircleCopyCircle(NULL, currentCircle, error);
+    require_quiet(prunedCircle, errOut);
+    require_quiet(SOSCirclePreGenerationSign(prunedCircle, userKey, error), errOut);
+
+    gencount = SOSGenerationIncrementAndCreate(SOSCircleGetGeneration(prunedCircle));
+
+    signature = SOSCircleCopyNextGenSignatureWithPeerAdded(prunedCircle, applicant, ourKey, error);
+    require_quiet(signature, errOut);
 
-    account->circle_rings_retirements_need_attention = false;
+    pbblob = SOSPiggyBackBlobCopyEncodedData(gencount, ourKey, signature, kCFAllocatorDefault, error);
+    
+errOut:
+    CFReleaseNull(gencount);
+    CFReleaseNull(signature);
+    CFReleaseNull(ourKey);
+
+    return pbblob;
 }
 
+bool SOSAccountJoinWithCircleJoiningBlob(SOSAccountRef account, CFDataRef joiningBlob, CFErrorRef *error) {
+    bool retval = false;
+    SecKeyRef userKey = NULL;
+    struct piggyBackBlob *pbb = NULL;
+
+    userKey = SOSAccountGetPrivateCredential(account, error);
+    require_quiet(userKey, errOut);
+    pbb = SOSPiggyBackBlobCreateFromData(kCFAllocatorDefault, joiningBlob, error);
+    require_quiet(pbb, errOut);
+
+    SOSAccountSetValue(account, kSOSUnsyncedViewsKey, kCFBooleanTrue, NULL);
+
+    retval = SOSAccountModifyCircle(account, error, ^bool(SOSCircleRef copyOfCurrent) {
+        return SOSCircleAcceptPeerFromHSA2(copyOfCurrent, userKey,
+                                           pbb->gencount,
+                                           pbb->pubKey,
+                                           pbb->signature,
+                                           account->my_identity, error);;
+
+    });
+    
+errOut:
+    if(pbb) {
+        CFReleaseNull(pbb->gencount);
+        CFReleaseNull(pbb->pubKey);
+        CFReleaseNull(pbb->signature);
+        free(pbb);
+    }
+    return retval;
+}
+
+static char boolToChars(bool val, char truechar, char falsechar) {
+    return val? truechar: falsechar;
+}
+
+#define ACCOUNTLOGSTATE "accountLogState"
+void SOSAccountLogState(SOSAccountRef account) {
+    bool hasPubKey = account->user_public != NULL;
+    bool pubTrusted = account->user_public_trusted;
+    bool hasPriv = account->_user_private != NULL;
+    SOSCCStatus stat = SOSAccountGetCircleStatus(account, NULL);
+    CFStringRef userPubKeyID =  (account->user_public) ? SOSCopyIDOfKeyWithLength(account->user_public, 8, NULL):
+            CFStringCreateCopy(kCFAllocatorDefault, CFSTR("*No Key*"));
+
+    secnotice(ACCOUNTLOGSTATE, "Start");
+
+    secnotice(ACCOUNTLOGSTATE, "ACCOUNT: [keyStatus: %c%c%c hpub %@] [SOSCCStatus: %@]",
+              boolToChars(hasPubKey, 'U', 'u'), boolToChars(pubTrusted, 'T', 't'), boolToChars(hasPriv, 'I', 'i'),
+              userPubKeyID,
+              SOSAccountGetSOSCCStatusString(stat)
+              );
+    CFReleaseNull(userPubKeyID);
+    if(account->trusted_circle)  SOSCircleLogState(ACCOUNTLOGSTATE, account->trusted_circle, account->user_public, SOSAccountGetMyPeerID(account));
+    else secnotice(ACCOUNTLOGSTATE, "ACCOUNT: No Circle");
+}
+
+void SOSAccountLogViewState(SOSAccountRef account) {
+    bool isInCircle = SOSAccountIsInCircle(account, NULL);
+    require_quiet(isInCircle, imOut);
+    SOSPeerInfoRef mpi = SOSAccountGetMyPeerInfo(account);
+    bool isInitialComplete = SOSAccountHasCompletedInitialSync(account);
+    bool isBackupComplete = SOSAccountHasCompletedRequiredBackupSync(account);
+
+    CFSetRef views = mpi ? SOSPeerInfoCopyEnabledViews(mpi) : NULL;
+    CFStringSetPerformWithDescription(views, ^(CFStringRef description) {
+        secnotice(ACCOUNTLOGSTATE, "Sync: %c%c PeerViews: %@",
+                  boolToChars(isInitialComplete, 'I', 'i'),
+                  boolToChars(isBackupComplete, 'B', 'b'),
+                  description);
+    });
+    CFReleaseNull(views);
+    CFSetRef unsyncedViews = SOSAccountCopyOutstandingViews(account);
+    CFStringSetPerformWithDescription(views, ^(CFStringRef description) {
+        secnotice(ACCOUNTLOGSTATE, "outstanding views: %@", description);
+    });
+    CFReleaseNull(unsyncedViews);
+
+imOut:
+    secnotice(ACCOUNTLOGSTATE, "Finish");
+
+    return;
+}