diff --git a/cmix/follow.go b/cmix/follow.go
index f9958e1c000cd6a508adf74d9b4f978dfe82ccef..aba9edb4ee24d30dc474663c21cf81ca47b88707 100644
--- a/cmix/follow.go
+++ b/cmix/follow.go
@@ -87,43 +87,50 @@ func (c *client) followNetwork(report ClientErrorReport,
 			stop.ToStopped()
 			return
 		case <-ticker.C:
+
+			operator := func(toTrack []receptionID.IdentityUse) error {
+
+				// set up tracking tools
+				wg := &sync.WaitGroup{}
+				wg.Add(len(toTrack))
+
+				// trigger the first separately because it will get network state
+				// updates
+				go func() {
+					c.follow(toTrack[0], report, rng, c.comms, stop, abandon,
+						true)
+					wg.Done()
+				}()
+
+				//trigger all others without getting network state updates
+				for i := 1; i < len(toTrack); i++ {
+					go func(index int) {
+						c.follow(toTrack[index], report, rng, c.comms, stop,
+							dummyAbandon, false)
+						wg.Done()
+					}(i)
+				}
+
+				//wait for all to complete
+				wg.Wait()
+				return nil
+			}
+
 			// get the list of identities to track
 			stream := c.rng.GetStream()
-			toTrack, err := c.Tracker.GetEphemeralIdentities(
+			err := c.Tracker.ForEach(
 				int(c.param.MaxParallelIdentityTracks),
 				stream,
-				c.Space.GetAddressSpaceWithoutWait())
+				c.Space.GetAddressSpaceWithoutWait(),
+				operator)
 			stream.Close()
 
 			if err != nil {
-				jww.ERROR.Printf("failed to get identities to track")
+				jww.ERROR.Printf("failed to operate on identities to "+
+					"track: %s", err)
 				continue
 			}
 
-			// set up tracking tools
-			wg := &sync.WaitGroup{}
-			wg.Add(len(toTrack))
-
-			// trigger the first separately because it will get network state
-			// updates
-			go func() {
-				c.follow(toTrack[0], report, rng, c.comms, stop, abandon,
-					true)
-				wg.Done()
-			}()
-
-			//trigger all others without getting network state updates
-			for i := 1; i < len(toTrack); i++ {
-				go func(index int) {
-					c.follow(toTrack[index], report, rng, c.comms, stop,
-						dummyAbandon, false)
-					wg.Done()
-				}(i)
-			}
-
-			//wait for all to complete
-			wg.Wait()
-
 		case <-TrackTicker.C:
 			numPolls := atomic.SwapUint64(c.tracker, 0)
 			if c.numLatencies != 0 {
diff --git a/cmix/identity/receptionID/store.go b/cmix/identity/receptionID/store.go
index 0054c3da0a1229bd814aa3b5fac8f662c4bceae7..78d125ac90c64f5dcc43ba63abdf1e318694f9a1 100644
--- a/cmix/identity/receptionID/store.go
+++ b/cmix/identity/receptionID/store.go
@@ -160,46 +160,13 @@ func (s *Store) makeStoredReferences() []storedReference {
 	return identities[:i]
 }
 
-// GetIdentity will return a single identity. If none are available, it will
-// return a fake one
-func (s *Store) GetIdentity(rng io.Reader, addressSize uint8) IdentityUse {
-	s.mux.Lock()
-	defer s.mux.Unlock()
-
-	now := netTime.Now()
-
-	// Remove any now expired identities
-	s.prune(now)
-
-	var identity IdentityUse
-	var err error
-
-	// If the list is empty, then return a randomly generated identity to poll
-	// with so that we can continue tracking the network and to further
-	// obfuscate network identities.
-	if len(s.active) == 0 {
-		identity, err = generateFakeIdentity(rng, addressSize, now)
-		if err != nil {
-			jww.FATAL.Panicf(
-				"Failed to generate a new ID when none available: %+v", err)
-		}
-	} else {
-		identity, err = s.selectIdentity(rng, now)
-		if err != nil {
-			jww.FATAL.Panicf("Failed to select an ID: %+v", err)
-		}
-	}
-
-	return identity
-}
-
-// GetIdentities will return up to 'n' identities randomly in a random order.
-// if no identities exist, it will return a single fake identity
-func (s *Store) GetIdentities(n int, rng io.Reader,
-	addressSize uint8) ([]IdentityUse, error) {
+// ForEach operates on 'n' identities randomly in a random order.
+// if no identities exist, it will operate on a single fake identity
+func (s *Store) ForEach(n int, rng io.Reader,
+	addressSize uint8, operate func([]IdentityUse) error) error {
 
 	if n < 1 {
-		return nil, InvalidRequestedNumIdentities
+		return InvalidRequestedNumIdentities
 	}
 
 	s.mux.Lock()
@@ -231,7 +198,8 @@ func (s *Store) GetIdentities(n int, rng io.Reader,
 		}
 	}
 
-	return identities, nil
+	// do the passed operation on all identities
+	return operate(identities)
 }
 
 func (s *Store) AddIdentity(identity Identity) error {
@@ -274,7 +242,8 @@ func (s *Store) RemoveIdentity(ephID ephemeral.Id) {
 	s.mux.Lock()
 	defer s.mux.Unlock()
 
-	for i, inQuestion := range s.active {
+	for i := 0; i < len(s.active); i++ {
+		inQuestion := s.active[i]
 		if inQuestion.EphId == ephID {
 			s.active = append(s.active[:i], s.active[i+1:]...)
 
@@ -290,6 +259,8 @@ func (s *Store) RemoveIdentity(ephID ephemeral.Id) {
 				}
 			}
 
+			i--
+
 			return
 		}
 	}
@@ -300,7 +271,8 @@ func (s *Store) RemoveIdentities(source *id.ID) {
 	defer s.mux.Unlock()
 
 	doSave := false
-	for i, inQuestion := range s.active {
+	for i := 0; i < len(s.active); i++ {
+		inQuestion := s.active[i]
 		if inQuestion.Source.Cmp(source) {
 			s.active = append(s.active[:i], s.active[i+1:]...)
 
@@ -310,6 +282,7 @@ func (s *Store) RemoveIdentities(source *id.ID) {
 			}
 
 			doSave = doSave || !inQuestion.Ephemeral
+			i--
 		}
 	}
 	if doSave {
diff --git a/cmix/identity/receptionID/store_test.go b/cmix/identity/receptionID/store_test.go
index 1ddcd8033f3af7afceca6f9c0bd9a47890a46bef..5161469cfd7c044eddade131e8f76b90c6c30678 100644
--- a/cmix/identity/receptionID/store_test.go
+++ b/cmix/identity/receptionID/store_test.go
@@ -151,38 +151,6 @@ func TestStore_makeStoredReferences(t *testing.T) {
 	}
 }
 
-func TestStore_GetIdentity(t *testing.T) {
-	kv := versioned.NewKV(ekv.MakeMemstore())
-	s := NewOrLoadStore(kv)
-	prng := rand.New(rand.NewSource(42))
-	testID, err := generateFakeIdentity(prng, 15, netTime.Now())
-	if err != nil {
-		t.Fatalf("Failed to generate fake ID: %+v", err)
-	}
-	if s.AddIdentity(testID.Identity) != nil {
-		t.Errorf("AddIdentity() produced an error: %+v", err)
-	}
-
-	idu := s.GetIdentity(prng, 15)
-
-	if !testID.Equal(idu.Identity) {
-		t.Errorf("GetIdentity() did not return the expected Identity."+
-			"\nexpected: %s\nreceived: %s", testID, idu)
-	}
-}
-
-func TestStore_GetIdentity_NoIdentities(t *testing.T) {
-	kv := versioned.NewKV(ekv.MakeMemstore())
-	s := NewOrLoadStore(kv)
-	prng := rand.New(rand.NewSource(42))
-
-	idu := s.GetIdentity(prng, 15)
-
-	if !idu.Fake {
-		t.Errorf("GetIdentity() did not return a fake identity")
-	}
-}
-
 func TestStore_GetIdentities(t *testing.T) {
 	kv := versioned.NewKV(ekv.MakeMemstore())
 	s := NewOrLoadStore(kv)
@@ -207,7 +175,12 @@ func TestStore_GetIdentities(t *testing.T) {
 	}
 
 	//get one
-	idu, err := s.GetIdentities(1, prng, 15)
+	var idu []IdentityUse
+	o := func(a []IdentityUse) error {
+		idu = a
+		return nil
+	}
+	err := s.ForEach(1, prng, 15, o)
 	if err != nil {
 		t.Errorf("GetIdentity() produced an error: %+v", err)
 	}
@@ -218,7 +191,7 @@ func TestStore_GetIdentities(t *testing.T) {
 	}
 
 	//get three
-	idu, err = s.GetIdentities(3, prng, 15)
+	err = s.ForEach(3, prng, 15, o)
 	if err != nil {
 		t.Errorf("GetIdentity() produced an error: %+v", err)
 	}
@@ -235,7 +208,7 @@ func TestStore_GetIdentities(t *testing.T) {
 	}
 
 	//get ten
-	idu, err = s.GetIdentities(10, prng, 15)
+	err = s.ForEach(10, prng, 15, o)
 	if err != nil {
 		t.Errorf("GetIdentity() produced an error: %+v", err)
 	}
@@ -252,7 +225,7 @@ func TestStore_GetIdentities(t *testing.T) {
 	}
 
 	//get fifty
-	idu, err = s.GetIdentities(50, prng, 15)
+	err = s.ForEach(50, prng, 15, o)
 	if err != nil {
 		t.Errorf("GetIdentity() produced an error: %+v", err)
 	}
@@ -269,7 +242,7 @@ func TestStore_GetIdentities(t *testing.T) {
 	}
 
 	//get 100
-	idu, err = s.GetIdentities(100, prng, 15)
+	err = s.ForEach(100, prng, 15, o)
 	if err != nil {
 		t.Errorf("GetIdentity() produced an error: %+v", err)
 	}
@@ -286,7 +259,7 @@ func TestStore_GetIdentities(t *testing.T) {
 	}
 
 	//get 1000, should only return 100
-	idu, err = s.GetIdentities(1000, prng, 15)
+	err = s.ForEach(1000, prng, 15, o)
 	if err != nil {
 		t.Errorf("GetIdentity() produced an error: %+v", err)
 	}
@@ -304,7 +277,13 @@ func TestStore_GetIdentities(t *testing.T) {
 
 	// get 100 a second time and make sure the order is not the same as a
 	// smoke test that the shuffle is working
-	idu2, err := s.GetIdentities(1000, prng, 15)
+	var idu2 []IdentityUse
+	o2 := func(a []IdentityUse) error {
+		idu2 = a
+		return nil
+	}
+
+	err = s.ForEach(1000, prng, 15, o2)
 	if err != nil {
 		t.Errorf("GetIdentity() produced an error: %+v", err)
 	}
@@ -329,7 +308,13 @@ func TestStore_GetIdentities_NoIdentities(t *testing.T) {
 	s := NewOrLoadStore(kv)
 	prng := rand.New(rand.NewSource(42))
 
-	idu, err := s.GetIdentities(5, prng, 15)
+	var idu []IdentityUse
+	o := func(a []IdentityUse) error {
+		idu = a
+		return nil
+	}
+
+	err := s.ForEach(5, prng, 15, o)
 	if err != nil {
 		t.Errorf("GetIdentities() produced an error: %+v", err)
 	}
@@ -350,27 +335,31 @@ func TestStore_GetIdentities_BadNum(t *testing.T) {
 	s := NewOrLoadStore(kv)
 	prng := rand.New(rand.NewSource(42))
 
-	_, err := s.GetIdentities(0, prng, 15)
+	o := func(a []IdentityUse) error {
+		return nil
+	}
+
+	err := s.ForEach(0, prng, 15, o)
 	if err == nil {
 		t.Errorf("GetIdentities() shoud error with bad num value")
 	}
 
-	_, err = s.GetIdentities(-1, prng, 15)
+	err = s.ForEach(-1, prng, 15, o)
 	if err == nil {
 		t.Errorf("GetIdentities() shoud error with bad num value")
 	}
 
-	_, err = s.GetIdentities(-100, prng, 15)
+	err = s.ForEach(-100, prng, 15, o)
 	if err == nil {
 		t.Errorf("GetIdentities() shoud error with bad num value")
 	}
 
-	_, err = s.GetIdentities(-1000000, prng, 15)
+	err = s.ForEach(-1000000, prng, 15, o)
 	if err == nil {
 		t.Errorf("GetIdentities() shoud error with bad num value")
 	}
 
-	_, err = s.GetIdentities(math.MinInt64, prng, 15)
+	err = s.ForEach(math.MinInt64, prng, 15, o)
 	if err == nil {
 		t.Errorf("GetIdentities() shoud error with bad num value")
 	}
diff --git a/cmix/identity/tracker.go b/cmix/identity/tracker.go
index 42c8b78d9d03d1277ebaf28b81323f0fff4c8dbd..fb51d79b7759f92e7a00ece8600c75470d9a3496 100644
--- a/cmix/identity/tracker.go
+++ b/cmix/identity/tracker.go
@@ -49,8 +49,8 @@ type Tracker interface {
 	StartProcesses() stoppable.Stoppable
 	AddIdentity(id *id.ID, validUntil time.Time, persistent bool)
 	RemoveIdentity(id *id.ID)
-	GetEphemeralIdentity(rng io.Reader, addressSize uint8) receptionID.IdentityUse
-	GetEphemeralIdentities(n int, rng io.Reader, addressSize uint8) ([]receptionID.IdentityUse, error)
+	ForEach(n int, rng io.Reader, addressSize uint8,
+		operator func([]receptionID.IdentityUse) error) error
 	GetIdentity(get *id.ID) (TrackedID, error)
 }
 
@@ -143,20 +143,14 @@ func (t *manager) RemoveIdentity(id *id.ID) {
 	t.deleteIdentity <- id
 }
 
-// GetEphemeralIdentity returns an ephemeral Identity to poll the network with.
-// It will return a fake identity if none are available.
-func (t *manager) GetEphemeralIdentity(rng io.Reader,
-	addressSize uint8) receptionID.IdentityUse {
-	return t.ephemeral.GetIdentity(rng, addressSize)
-}
-
-// GetEphemeralIdentities returns a fisher-yates shuffled list of up to 'num'
-// ephemeral identities. It will return a fake identity if none are available
+// ForEach passes a fisher-yates shuffled list of up to 'num'
+// ephemeral identities into the operation function. It will pass a
+// fake identity if none are available
 // and less than 'num' if less than 'num' are available.
 // 'num' must be positive non-zero
-func (t *manager) GetEphemeralIdentities(n int, rng io.Reader, addressSize uint8) (
-	[]receptionID.IdentityUse, error) {
-	return t.ephemeral.GetIdentities(n, rng, addressSize)
+func (t *manager) ForEach(n int, rng io.Reader, addressSize uint8,
+	operator func([]receptionID.IdentityUse) error) error {
+	return t.ephemeral.ForEach(n, rng, addressSize, operator)
 }
 
 // GetIdentity returns a currently tracked identity