diff --git a/channels/adminListener.go b/channels/adminListener.go
index d9e203f1c0c4bab8be5fa14220130c773fd74ee5..86f80142ef5345a3aad346907d96940097a281a6 100644
--- a/channels/adminListener.go
+++ b/channels/adminListener.go
@@ -15,6 +15,7 @@ import (
 	"gitlab.com/elixxir/crypto/channel"
 	"gitlab.com/elixxir/primitives/states"
 	"gitlab.com/xx_network/primitives/id"
+	"time"
 )
 
 // adminListener adheres to the [broadcast.ListenerFunc] interface and is used
@@ -54,8 +55,10 @@ func (al *adminListener) Listen(payload []byte,
 		return
 	}
 
-	// Modify the timestamp to reduce the chance message order will be ambiguous
-	ts := mutateTimestamp(round.Timestamps[states.QUEUED], msgID)
+	// Replace the timestamp on the message if it is outside of the
+	// allowable range
+	ts := vetTimestamp(time.Unix(0, cm.LocalTimestamp),
+		round.Timestamps[states.QUEUED], msgID)
 
 	// Submit the message to the event model for listening
 	if uuid, err := al.trigger(al.chID, cm, ts, msgID, receptionID,
diff --git a/channels/mutateTimestamp.go b/channels/mutateTimestamp.go
index 47c2bf1f02da14eaf829f37cb5b77970aaebf394..8b8bf683e971635458124783b76754fa9e3638b5 100644
--- a/channels/mutateTimestamp.go
+++ b/channels/mutateTimestamp.go
@@ -18,10 +18,28 @@ const (
 	// arise due to cofactors with the message ID when doing the modulo
 	tenMsInNs     = 10000019
 	halfTenMsInNs = tenMsInNs / 2
+	beforeGrace   = 5 * time.Second
+	afterGrace    = 2 * time.Second
 )
 
 var tenMsInNsLargeInt = large.NewInt(tenMsInNs)
 
+// vetTimestamp determines which timestamp to use for a message. It will
+// use the local timestamp provided in the message as long as it is within 5
+// seconds before the round and 2 second after the round. Otherwise, it will
+// use the round timestamp via mutateTimestamp
+func vetTimestamp(localTS, ts time.Time, msgID channel.MessageID) time.Time {
+
+	before := ts.Add(-beforeGrace)
+	after := ts.Add(afterGrace)
+
+	if localTS.Before(before) || localTS.After(after) {
+		return mutateTimestamp(ts, msgID)
+	}
+
+	return localTS
+}
+
 // mutateTimestamp is used to modify the the timestamps on all messages in a
 // deterministic manner. This is because message ordering is done by timestamp
 // and the timestamps come from the rounds, which means multiple messages can
diff --git a/channels/mutateTimestamp_test.go b/channels/mutateTimestamp_test.go
index 120fd44156c2c1616a876d60db27725caebd6eaf..93561868b10ac781ef2c86cc0b483a0fed1815f8 100644
--- a/channels/mutateTimestamp_test.go
+++ b/channels/mutateTimestamp_test.go
@@ -53,3 +53,77 @@ func TestMutateTimestampDeltaAverage(t *testing.T) {
 		t.Fatal()
 	}
 }
+
+const generationRange = beforeGrace + afterGrace
+
+// TestVetTimestamp_Happy tests that when the localTS is within
+// the allowed range, it is unmodified
+func TestVetTimestamp_Happy(t *testing.T) {
+	samples := 10000
+
+	rng := rand.New(rand.NewSource(netTime.Now().UnixNano()))
+
+	for i := 0; i < samples; i++ {
+
+		now := time.Now()
+
+		tested := now.Add(-beforeGrace).Add(time.Duration(rng.Int63()) % generationRange)
+
+		var msgID channel.MessageID
+		rng.Read(msgID[:])
+
+		result := vetTimestamp(tested, now, msgID)
+
+		if !tested.Equal(result) {
+			t.Errorf("Timestamp was molested unexpectedly")
+		}
+	}
+}
+
+// TestVetTimestamp_Happy tests that when the localTS is less than
+// the allowed time period it is replaced
+func TestVetTimestamp_BeforePeriod(t *testing.T) {
+	samples := 10000
+
+	rng := rand.New(rand.NewSource(netTime.Now().UnixNano()))
+
+	for i := 0; i < samples; i++ {
+
+		now := time.Now()
+
+		tested := now.Add(-beforeGrace).Add(-time.Duration(rng.Int63()) % (100000 * time.Hour))
+
+		var msgID channel.MessageID
+		rng.Read(msgID[:])
+
+		result := vetTimestamp(tested, now, msgID)
+
+		if tested.Equal(result) {
+			t.Errorf("Timestamp was unmolested unexpectedly")
+		}
+	}
+}
+
+// TestVetTimestamp_Happy tests that when the localTS is greater than
+// the allowed time period it is replaced
+func TestVetTimestamp_AfterPeriod(t *testing.T) {
+	samples := 10000
+
+	rng := rand.New(rand.NewSource(netTime.Now().UnixNano()))
+
+	for i := 0; i < samples; i++ {
+
+		now := time.Now()
+
+		tested := now.Add(afterGrace).Add(-time.Duration(rng.Int63()) % (100000 * time.Hour))
+
+		var msgID channel.MessageID
+		rng.Read(msgID[:])
+
+		result := vetTimestamp(tested, now, msgID)
+
+		if tested.Equal(result) {
+			t.Errorf("Timestamp was unmolested unexpectedly")
+		}
+	}
+}
diff --git a/channels/send.go b/channels/send.go
index 7b46152bb07b801fcd23fcd649829065d4e654db..0f8cfd7d5e4bab5a355732bb80f6e908c79e8219 100644
--- a/channels/send.go
+++ b/channels/send.go
@@ -17,6 +17,7 @@ import (
 	"gitlab.com/elixxir/crypto/rsa"
 	"gitlab.com/xx_network/primitives/id"
 	"gitlab.com/xx_network/primitives/id/ephemeral"
+	"gitlab.com/xx_network/primitives/netTime"
 	"google.golang.org/protobuf/proto"
 	"time"
 )
@@ -50,11 +51,12 @@ func (m *manager) SendGeneric(channelID *id.ID, messageType MessageType,
 	var msgId cryptoChannel.MessageID
 
 	chMsg := &ChannelMessage{
-		Lease:       validUntil.Nanoseconds(),
-		PayloadType: uint32(messageType),
-		Payload:     msg,
-		Nickname:    nickname,
-		Nonce:       make([]byte, messageNonceSize),
+		Lease:          validUntil.Nanoseconds(),
+		PayloadType:    uint32(messageType),
+		Payload:        msg,
+		Nickname:       nickname,
+		Nonce:          make([]byte, messageNonceSize),
+		LocalTimestamp: netTime.Now().UnixNano(),
 	}
 
 	// Generate random nonce to be used for message ID generation. This makes it
@@ -75,7 +77,7 @@ func (m *manager) SendGeneric(channelID *id.ID, messageType MessageType,
 		ECCPublicKey: m.me.PubKey,
 	}
 
-	//Note: we are not checking check if message is too long before trying to
+	//Note: we are not checking if message is too long before trying to
 	//find a round
 
 	//Build the function pointer that will build the message
@@ -145,11 +147,12 @@ func (m *manager) SendAdminGeneric(privKey rsa.PrivateKey, channelID *id.ID,
 
 	var msgId cryptoChannel.MessageID
 	chMsg := &ChannelMessage{
-		Lease:       validUntil.Nanoseconds(),
-		PayloadType: uint32(messageType),
-		Payload:     msg,
-		Nickname:    AdminUsername,
-		Nonce:       make([]byte, messageNonceSize),
+		Lease:          validUntil.Nanoseconds(),
+		PayloadType:    uint32(messageType),
+		Payload:        msg,
+		Nickname:       AdminUsername,
+		Nonce:          make([]byte, messageNonceSize),
+		LocalTimestamp: netTime.Now().UnixNano(),
 	}
 
 	// Generate random nonce to be used for message ID generation. This makes it
diff --git a/channels/userListener.go b/channels/userListener.go
index dad5075fd08daf0a46f2a221108df2b90dc45a91..202e5fa7d93b4ba15485cc2549806e0b8b014dea 100644
--- a/channels/userListener.go
+++ b/channels/userListener.go
@@ -14,6 +14,7 @@ import (
 	"gitlab.com/elixxir/client/cmix/rounds"
 	"gitlab.com/elixxir/primitives/states"
 	"gitlab.com/xx_network/primitives/id"
+	"time"
 )
 
 // the userListener adheres to the [broadcast.ListenerFunc] interface and is
@@ -64,8 +65,9 @@ func (ul *userListener) Listen(payload []byte,
 		return
 	}
 
-	// Modify the timestamp to reduce the chance message order will be ambiguous
-	ts := mutateTimestamp(round.Timestamps[states.QUEUED], msgID)
+	// Replace the timestamp on the message if it is outside of the
+	// allowable range
+	ts := vetTimestamp(time.Unix(0, cm.LocalTimestamp), round.Timestamps[states.QUEUED], msgID)
 
 	//TODO: Processing of the message relative to admin commands will be here