Repository: Avarok-Cybersecurity/Citadel-Protocol (language: rust)

Issue:
Credentialed Auth: Allow customizable password requirements



Discussion / hints:

Fix patch:
diff --git a/Makefile.toml b/Makefile.toml
index 781275ae3..14f2dd1cb 100644
--- a/Makefile.toml
+++ b/Makefile.toml
@@ -8,7 +8,11 @@ args = ["clean"]
 
 [tasks.clippy]
 command = "cargo"
-args = ["clippy"]
+args = ["clippy", "--all"]
+
+[tasks.clippy-tests]
+command = "cargo"
+args = ["clippy", "--all", "--tests", "--features=localhost-testing,multi-threaded"]
 
 [tasks.fmt]
 command = "cargo"
@@ -29,7 +33,7 @@ args = ["doc", "--package", "citadel_sdk", "--features=webrtc,doc-images", "--no
 dependencies = ["git_lfs"]
 
 [tasks.pr]
-dependencies = ["fmt", "clippy"]
+dependencies = ["fmt", "clippy", "clippy-tests"]
 
 [tasks.install-binstall]
 install_crate = { crate_name = "cargo-binstall", binary = "cargo", test_arg = ["binstall", "--help"] }
diff --git a/citadel_proto/src/error.rs b/citadel_proto/src/error.rs
index b45154ce8..0ce9e7f96 100644
--- a/citadel_proto/src/error.rs
+++ b/citadel_proto/src/error.rs
@@ -1,3 +1,4 @@
+use crate::prelude::NodeRequest;
 use citadel_crypt::misc::CryptError;
 use citadel_user::misc::AccountError;
 use std::error::Error;
@@ -21,6 +22,11 @@ pub enum NetworkError {
     InternalError(&'static str),
     /// For a converted error
     Generic(String),
+    /// for remote send errors
+    NodeRemoteSendError {
+        request: Box<NodeRequest>,
+        reason: String,
+    },
     ///
     ProperShutdown,
 }
@@ -49,6 +55,7 @@ impl NetworkError {
             NetworkError::InvalidRequest(err) => (*err).to_string(),
             NetworkError::InvalidPacket(err) => (*err).to_string(),
             NetworkError::ProperShutdown => "Proper shutdown called".to_string(),
+            NetworkError::NodeRemoteSendError { reason, .. } => reason.clone(),
         }
     }
 
@@ -66,6 +73,7 @@ impl NetworkError {
             }
             NetworkError::InvalidRequest(err) => err.to_string(),
             NetworkError::InvalidPacket(err) => err.to_string(),
+            NetworkError::NodeRemoteSendError { reason, .. } => reason,
             NetworkError::ProperShutdown => {
                 format!("{:?}", NetworkError::ProperShutdown)
             }
diff --git a/citadel_proto/src/kernel/kernel_trait.rs b/citadel_proto/src/kernel/kernel_trait.rs
index ec35454c3..4e90571ff 100644
--- a/citadel_proto/src/kernel/kernel_trait.rs
+++ b/citadel_proto/src/kernel/kernel_trait.rs
@@ -8,7 +8,7 @@ use auto_impl::auto_impl;
 /// The [NetKernel] is the thread-safe interface between the single-threaded OR multi-threaded async
 /// protocol and your network application
 #[async_trait]
-#[auto_impl(Box)]
+#[auto_impl(Box, &mut)]
 pub trait NetKernel: Send + Sync {
     /// when the kernel executes, it will be given a handle to the server
     fn load_remote(&mut self, node_remote: NodeRemote) -> Result<(), NetworkError>;
diff --git a/citadel_proto/src/proto/node.rs b/citadel_proto/src/proto/node.rs
index 96be5a233..cf8d70468 100644
--- a/citadel_proto/src/proto/node.rs
+++ b/citadel_proto/src/proto/node.rs
@@ -707,7 +707,7 @@ impl HdpServer {
             }
         };
 
-        while let Some((outbound_request, ticket_id)) = outbound_send_request_rx.next().await {
+        while let Some((outbound_request, ticket_id)) = outbound_send_request_rx.recv().await {
             match outbound_request {
                 NodeRequest::GroupBroadcastCommand(GroupBroadcastCommand {
                     implicated_cid,
diff --git a/citadel_proto/src/proto/node_request.rs b/citadel_proto/src/proto/node_request.rs
index 57ad7726b..e5ad44f81 100644
--- a/citadel_proto/src/proto/node_request.rs
+++ b/citadel_proto/src/proto/node_request.rs
@@ -7,6 +7,7 @@ use citadel_crypt::misc::TransferType;
 use citadel_crypt::prelude::SecurityLevel;
 use citadel_crypt::streaming_crypt_scrambler::ObjectSource;
 use citadel_user::auth::proposed_credentials::ProposedCredentials;
+use std::fmt::{Debug, Formatter};
 use std::net::SocketAddr;
 use std::path::PathBuf;
 
@@ -99,3 +100,9 @@ pub enum NodeRequest {
     /// shutdown signal
     Shutdown,
 }
+
+impl Debug for NodeRequest {
+    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
+        write!(f, "NodeRequest")
+    }
+}
diff --git a/citadel_proto/src/proto/outbound_sender.rs b/citadel_proto/src/proto/outbound_sender.rs
index f7247c230..4a0ee6f62 100644
--- a/citadel_proto/src/proto/outbound_sender.rs
+++ b/citadel_proto/src/proto/outbound_sender.rs
@@ -147,21 +147,26 @@ impl std::fmt::Debug for OutboundUdpSender {
 }
 
 /// As asynchronous channel meant to rate-limit input
-pub struct BoundedSender<T>(futures::channel::mpsc::Sender<T>);
+pub struct BoundedSender<T>(tokio::sync::mpsc::Sender<T>);
 
-pub type BoundedReceiver<T> = futures::channel::mpsc::Receiver<T>;
+pub type BoundedReceiver<T> = tokio::sync::mpsc::Receiver<T>;
 
 impl<T> BoundedSender<T> {
     /// Creates a new bounded channel
-    pub fn new(limit: usize) -> (BoundedSender<T>, futures::channel::mpsc::Receiver<T>) {
-        let (tx, rx) = futures::channel::mpsc::channel(limit);
+    pub fn new(limit: usize) -> (BoundedSender<T>, BoundedReceiver<T>) {
+        let (tx, rx) = tokio::sync::mpsc::channel(limit);
         (Self(tx), rx)
     }
 
     /// Attempts to send a value through the stream non-blocking and synchronously
-    pub fn try_send(&mut self, t: T) -> Result<(), futures::channel::mpsc::TrySendError<T>> {
+    pub fn try_send(&self, t: T) -> Result<(), tokio::sync::mpsc::error::TrySendError<T>> {
         self.0.try_send(t)
     }
+
+    /// Sends a value through the channel
+    pub async fn send(&self, t: T) -> Result<(), tokio::sync::mpsc::error::SendError<T>> {
+        self.0.send(t).await
+    }
 }
 
 impl<T> Clone for BoundedSender<T> {
@@ -170,30 +175,6 @@ impl<T> Clone for BoundedSender<T> {
     }
 }
 
-impl<T> Sink<T> for BoundedSender<T> {
-    type Error = NetworkError;
-
-    fn poll_ready(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
-        <futures::channel::mpsc::Sender<T> as Sink<T>>::poll_ready(Pin::new(&mut self.0), cx)
-            .map_err(|err| NetworkError::Generic(err.to_string()))
-    }
-
-    fn start_send(mut self: Pin<&mut Self>, item: T) -> Result<(), Self::Error> {
-        <futures::channel::mpsc::Sender<T> as Sink<T>>::start_send(Pin::new(&mut self.0), item)
-            .map_err(|err| NetworkError::Generic(err.to_string()))
-    }
-
-    fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
-        <futures::channel::mpsc::Sender<T> as Sink<T>>::poll_flush(Pin::new(&mut self.0), cx)
-            .map_err(|err| NetworkError::Generic(err.to_string()))
-    }
-
-    fn poll_close(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
-        <futures::channel::mpsc::Sender<T> as Sink<T>>::poll_close(Pin::new(&mut self.0), cx)
-            .map_err(|err| NetworkError::Generic(err.to_string()))
-    }
-}
-
 impl<T> Sink<T> for UnboundedSender<T> {
     type Error = NetworkError;
 
diff --git a/citadel_proto/src/proto/remote.rs b/citadel_proto/src/proto/remote.rs
index 90d25df8c..31e14b5aa 100644
--- a/citadel_proto/src/proto/remote.rs
+++ b/citadel_proto/src/proto/remote.rs
@@ -5,14 +5,11 @@ use crate::proto::node::HdpServerRemoteInner;
 use crate::proto::outbound_sender::BoundedSender;
 use citadel_user::account_manager::AccountManager;
 use citadel_wire::hypernode_type::NodeType;
-use futures::channel::mpsc::TrySendError;
-use futures::{Sink, SinkExt};
 use serde::{Deserialize, Serialize};
 use std::fmt::{Debug, Display, Formatter};
-use std::pin::Pin;
 use std::sync::Arc;
-use std::task::{Context, Poll};
 use std::time::Duration;
+use tokio::sync::mpsc::error::TrySendError;
 
 /// allows convenient communication with the server
 #[derive(Clone)]
@@ -22,9 +19,9 @@ pub struct NodeRemote {
 }
 
 #[async_trait::async_trait]
-#[auto_impl::auto_impl(Box, &mut)]
+#[auto_impl::auto_impl(Box, &mut, &, Arc)]
 pub trait Remote: Clone + Send {
-    async fn send(&mut self, request: NodeRequest) -> Result<Ticket, NetworkError> {
+    async fn send(&self, request: NodeRequest) -> Result<Ticket, NetworkError> {
         let ticket = self.get_next_ticket();
         self.send_with_custom_ticket(ticket, request)
             .await
@@ -32,27 +29,27 @@ pub trait Remote: Clone + Send {
     }
 
     async fn send_with_custom_ticket(
-        &mut self,
+        &self,
         ticket: Ticket,
         request: NodeRequest,
     ) -> Result<(), NetworkError>;
     async fn send_callback_subscription(
-        &mut self,
+        &self,
         request: NodeRequest,
     ) -> Result<KernelStreamSubscription, NetworkError>;
-    async fn send_callback(&mut self, request: NodeRequest) -> Result<NodeResult, NetworkError>;
+    async fn send_callback(&self, request: NodeRequest) -> Result<NodeResult, NetworkError>;
     fn account_manager(&self) -> &AccountManager;
     fn get_next_ticket(&self) -> Ticket;
 }
 
 #[async_trait::async_trait]
 impl Remote for NodeRemote {
-    async fn send(&mut self, request: NodeRequest) -> Result<Ticket, NetworkError> {
+    async fn send(&self, request: NodeRequest) -> Result<Ticket, NetworkError> {
         NodeRemote::send(self, request).await
     }
 
     async fn send_with_custom_ticket(
-        &mut self,
+        &self,
         ticket: Ticket,
         request: NodeRequest,
     ) -> Result<(), NetworkError> {
@@ -60,13 +57,13 @@ impl Remote for NodeRemote {
     }
 
     async fn send_callback_subscription(
-        &mut self,
+        &self,
         request: NodeRequest,
     ) -> Result<KernelStreamSubscription, NetworkError> {
         NodeRemote::send_callback_subscription(self, request).await
     }
 
-    async fn send_callback(&mut self, request: NodeRequest) -> Result<NodeResult, NetworkError> {
+    async fn send_callback(&self, request: NodeRequest) -> Result<NodeResult, NetworkError> {
         NodeRemote::send_callback(self, request).await
     }
 
@@ -86,7 +83,7 @@ impl Debug for NodeRemote {
 }
 
 impl NodeRemote {
-    /// Creates a new [HdpServerRemote]
+    /// Creates a new [`NodeRemote`]
     pub(crate) fn new(
         outbound_send_request_tx: BoundedSender<(NodeRequest, Ticket)>,
         callback_handler: KernelAsyncCallbackHandler,
@@ -106,16 +103,25 @@ impl NodeRemote {
 
     /// Especially used to keep track of a conversation (b/c a certain ticket number may be expected)
     pub async fn send_with_custom_ticket(
-        &mut self,
+        &self,
         ticket: Ticket,
         request: NodeRequest,
     ) -> Result<(), NetworkError> {
-        self.outbound_send_request_tx.send((request, ticket)).await
+        self.outbound_send_request_tx
+            .send((request, ticket))
+            .await
+            .map_err(|err| {
+                let reason = err.to_string();
+                NetworkError::NodeRemoteSendError {
+                    reason,
+                    request: Box::new(err.0 .0),
+                }
+            })
     }
 
     /// Sends a request to the HDP server. This should always be used to communicate with the server
     /// in order to obtain a ticket
-    pub async fn send(&mut self, request: NodeRequest) -> Result<Ticket, NetworkError> {
+    pub async fn send(&self, request: NodeRequest) -> Result<Ticket, NetworkError> {
         let ticket = self.get_next_ticket();
         self.send_with_custom_ticket(ticket, request)
             .await
@@ -124,7 +130,7 @@ impl NodeRemote {
 
     /// Returns an error if the ticket is already registered for a callback
     pub async fn send_callback_custom_ticket(
-        &mut self,
+        &self,
         request: NodeRequest,
         ticket: Ticket,
     ) -> Result<NodeResult, NetworkError> {
@@ -143,7 +149,7 @@ impl NodeRemote {
 
     /// Returns an error if the ticket is already registered for a stream-callback
     pub(crate) async fn send_callback_subscription_custom_ticket(
-        &mut self,
+        &self,
         request: NodeRequest,
         ticket: Ticket,
     ) -> Result<KernelStreamSubscription, NetworkError> {
@@ -160,7 +166,7 @@ impl NodeRemote {
 
     /// Convenience method for sending and awaiting for a response for the related ticket
     pub async fn send_callback_subscription(
-        &mut self,
+        &self,
         request: NodeRequest,
     ) -> Result<KernelStreamSubscription, NetworkError> {
         let ticket = self.get_next_ticket();
@@ -169,17 +175,14 @@ impl NodeRemote {
     }
 
     /// Convenience method for sending and awaiting for a response for the related ticket
-    pub async fn send_callback(
-        &mut self,
-        request: NodeRequest,
-    ) -> Result<NodeResult, NetworkError> {
+    pub async fn send_callback(&self, request: NodeRequest) -> Result<NodeResult, NetworkError> {
         let ticket = self.get_next_ticket();
         self.send_callback_custom_ticket(request, ticket).await
     }
 
     /// Convenience method for sending and awaiting for a response for the related ticket (with a timeout)
     pub async fn send_callback_timeout(
-        &mut self,
+        &self,
         request: NodeRequest,
         timeout: Duration,
     ) -> Result<NodeResult, NetworkError> {
@@ -189,9 +192,9 @@ impl NodeRemote {
     }
 
     /// Safely shutsdown the internal server
-    pub async fn shutdown(&mut self) -> Result<(), NetworkError> {
+    pub async fn shutdown(&self) -> Result<(), NetworkError> {
         let _ = self.send(NodeRequest::Shutdown).await?;
-        self.outbound_send_request_tx.close().await
+        Ok(())
     }
 
     // Note: when two nodes create a ticket, there may be equivalent values
@@ -202,7 +205,7 @@ impl NodeRemote {
 
     #[allow(clippy::result_large_err)]
     pub fn try_send_with_custom_ticket(
-        &mut self,
+        &self,
         ticket: Ticket,
         request: NodeRequest,
     ) -> Result<(), TrySendError<(NodeRequest, Ticket)>> {
@@ -211,7 +214,7 @@ impl NodeRemote {
 
     #[allow(clippy::result_large_err)]
     pub fn try_send(
-        &mut self,
+        &self,
         request: NodeRequest,
     ) -> Result<(), TrySendError<(NodeRequest, Ticket)>> {
         let ticket = self.get_next_ticket();
@@ -229,54 +232,6 @@ impl NodeRemote {
 
 impl Unpin for NodeRemote {}
 
-impl Sink<(Ticket, NodeRequest)> for NodeRemote {
-    type Error = NetworkError;
-
-    fn poll_ready(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
-        <Self as Sink<NodeRequest>>::poll_ready(self, cx)
-    }
-
-    fn start_send(
-        mut self: Pin<&mut Self>,
-        item: (Ticket, NodeRequest),
-    ) -> Result<(), Self::Error> {
-        Pin::new(&mut self.outbound_send_request_tx).start_send((item.1, item.0))
-    }
-
-    fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
-        <Self as Sink<NodeRequest>>::poll_flush(self, cx)
-    }
-
-    fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
-        <Self as Sink<NodeRequest>>::poll_close(self, cx)
-    }
-}
-
-impl Sink<NodeRequest> for NodeRemote {
-    type Error = NetworkError;
-
-    fn poll_ready(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
-        Pin::new(&mut self.outbound_send_request_tx).poll_ready(cx)
-    }
-
-    fn start_send(mut self: Pin<&mut Self>, item: NodeRequest) -> Result<(), Self::Error> {
-        let ticket = self.get_next_ticket();
-        Pin::new(&mut self.outbound_send_request_tx).start_send((item, ticket))
-    }
-
-    fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
-        Pin::new(&mut self.outbound_send_request_tx)
-            .poll_flush(cx)
-            .map_err(|err| NetworkError::Generic(err.to_string()))
-    }
-
-    fn poll_close(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
-        Pin::new(&mut self.outbound_send_request_tx)
-            .poll_close(cx)
-            .map_err(|err| NetworkError::Generic(err.to_string()))
-    }
-}
-
 /// A type sent through the server when a request is made
 #[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Serialize, Deserialize)]
 pub struct Ticket(pub u128);
diff --git a/citadel_sdk/src/backend_kv_store.rs b/citadel_sdk/src/backend_kv_store.rs
index 4efd461ec..ece8e3568 100644
--- a/citadel_sdk/src/backend_kv_store.rs
+++ b/citadel_sdk/src/backend_kv_store.rs
@@ -9,7 +9,7 @@ const DATA_MAP_KEY: &str = "_INTERNAL_DATA_MAP";
 /// for this particular connection
 pub trait BackendHandler: TargetLockedRemote {
     /// Gets a value from the backend
-    async fn get(&mut self, key: &str) -> Result<Option<Vec<u8>>, NetworkError> {
+    async fn get(&self, key: &str) -> Result<Option<Vec<u8>>, NetworkError> {
         let (session_cid, peer_cid) = self.get_cids();
         self.remote()
             .account_manager()
@@ -19,7 +19,7 @@ pub trait BackendHandler: TargetLockedRemote {
             .map_err(|err| NetworkError::msg(err.into_string()))
     }
     /// Removes a value from the backend, returning the previous value
-    async fn remove(&mut self, key: &str) -> Result<Option<Vec<u8>>, NetworkError> {
+    async fn remove(&self, key: &str) -> Result<Option<Vec<u8>>, NetworkError> {
         let (session_cid, peer_cid) = self.get_cids();
         self.remote()
             .account_manager()
@@ -29,7 +29,7 @@ pub trait BackendHandler: TargetLockedRemote {
             .map_err(|err| NetworkError::msg(err.into_string()))
     }
     /// Stores a value in the backend, either creating or overwriting any pre-existing value
-    async fn set(&mut self, key: &str, value: Vec<u8>) -> Result<Option<Vec<u8>>, NetworkError> {
+    async fn set(&self, key: &str, value: Vec<u8>) -> Result<Option<Vec<u8>>, NetworkError> {
         let (session_cid, peer_cid) = self.get_cids();
         self.remote()
             .account_manager()
@@ -39,7 +39,7 @@ pub trait BackendHandler: TargetLockedRemote {
             .map_err(|err| NetworkError::msg(err.into_string()))
     }
     /// Obtains the K,V map for this application
-    async fn get_all(&mut self) -> Result<HashMap<String, Vec<u8>>, NetworkError> {
+    async fn get_all(&self) -> Result<HashMap<String, Vec<u8>>, NetworkError> {
         let (session_cid, peer_cid) = self.get_cids();
         self.remote()
             .account_manager()
@@ -49,7 +49,7 @@ pub trait BackendHandler: TargetLockedRemote {
             .map_err(|err| NetworkError::msg(err.into_string()))
     }
     /// Obtains a list of K,V pairs such that `needle` is a subset of the K value
-    async fn remove_all(&mut self) -> Result<HashMap<String, Vec<u8>>, NetworkError> {
+    async fn remove_all(&self) -> Result<HashMap<String, Vec<u8>>, NetworkError> {
         let (session_cid, peer_cid) = self.get_cids();
         self.remote()
             .account_manager()
diff --git a/citadel_sdk/src/builder/node_builder.rs b/citadel_sdk/src/builder/node_builder.rs
index 8cc5879b6..d308aab15 100644
--- a/citadel_sdk/src/builder/node_builder.rs
+++ b/citadel_sdk/src/builder/node_builder.rs
@@ -173,7 +173,7 @@ impl NodeBuilder {
         self
     }
 
-    /// Attaches miscellaneous server settings (e.g., passwordless mode)
+    /// Attaches miscellaneous server settings (e.g., passwordless mode, credential requirements)
     pub fn with_server_misc_settings(&mut self, misc_settings: ServerMiscSettings) -> &mut Self {
         self.server_misc_settings = Some(misc_settings);
         self
diff --git a/citadel_sdk/src/fs.rs b/citadel_sdk/src/fs.rs
index f43359122..736e9a66e 100644
--- a/citadel_sdk/src/fs.rs
+++ b/citadel_sdk/src/fs.rs
@@ -5,7 +5,7 @@ use std::path::PathBuf;
 
 /// Writes a file or BytesSource to the Remote Encrypted Virtual Filesystem
 pub async fn write<T: ObjectSource, R: Into<PathBuf> + Send>(
-    remote: &mut impl TargetLockedRemote,
+    remote: &impl TargetLockedRemote,
     source: T,
     virtual_path: R,
 ) -> Result<(), NetworkError> {
@@ -14,7 +14,7 @@ pub async fn write<T: ObjectSource, R: Into<PathBuf> + Send>(
 
 /// Writes a file or BytesSource to the Remote Encrypted Virtual Filesystem with a custom security level.
 pub async fn write_with_security_level<T: ObjectSource, R: Into<PathBuf> + Send>(
-    remote: &mut impl TargetLockedRemote,
+    remote: &impl TargetLockedRemote,
     source: T,
     security_level: SecurityLevel,
     virtual_path: R,
@@ -26,7 +26,7 @@ pub async fn write_with_security_level<T: ObjectSource, R: Into<PathBuf> + Send>
 
 /// Reads a file from the Remote Encrypted Virtual Filesystem
 pub async fn read<R: Into<PathBuf> + Send>(
-    remote: &mut impl TargetLockedRemote,
+    remote: &impl TargetLockedRemote,
     virtual_path: R,
 ) -> Result<PathBuf, NetworkError> {
     read_with_security_level(remote, Default::default(), virtual_path).await
@@ -34,7 +34,7 @@ pub async fn read<R: Into<PathBuf> + Send>(
 
 /// Reads a file from the Remote Encrypted Virtual Filesystem with a custom transport security level
 pub async fn read_with_security_level<R: Into<PathBuf> + Send>(
-    remote: &mut impl TargetLockedRemote,
+    remote: &impl TargetLockedRemote,
     transfer_security_level: SecurityLevel,
     virtual_path: R,
 ) -> Result<PathBuf, NetworkError> {
@@ -45,7 +45,7 @@ pub async fn read_with_security_level<R: Into<PathBuf> + Send>(
 
 /// Takes a file from the Remote Encrypted Virtual Filesystem
 pub async fn take<R: Into<PathBuf> + Send>(
-    remote: &mut impl TargetLockedRemote,
+    remote: &impl TargetLockedRemote,
     virtual_path: R,
 ) -> Result<PathBuf, NetworkError> {
     remote
@@ -55,7 +55,7 @@ pub async fn take<R: Into<PathBuf> + Send>(
 
 /// Takes a file from the Remote Encrypted Virtual Filesystem with a custom security level.
 pub async fn take_with_security_level<R: Into<PathBuf> + Send>(
-    remote: &mut impl TargetLockedRemote,
+    remote: &impl TargetLockedRemote,
     transfer_security_level: SecurityLevel,
     virtual_path: R,
 ) -> Result<PathBuf, NetworkError> {
@@ -66,7 +66,7 @@ pub async fn take_with_security_level<R: Into<PathBuf> + Send>(
 
 /// Deletes a file from the Remote Encrypted Virtual Filesystem
 pub async fn delete<R: Into<PathBuf> + Send>(
-    remote: &mut impl TargetLockedRemote,
+    remote: &impl TargetLockedRemote,
     virtual_path: R,
 ) -> Result<(), NetworkError> {
     remote
@@ -136,12 +136,12 @@ mod tests {
             server_addr,
             UdpMode::Disabled,
             session_security_settings,
-            |_channel, mut remote| async move {
+            |_channel, remote| async move {
                 log::trace!(target: "citadel", "***CLIENT LOGIN SUCCESS :: File transfer next ***");
                 let virtual_path = PathBuf::from("/home/john.doe/TheBridge.pdf");
                 // write to file to the RE-VFS
                 crate::fs::write_with_security_level(
-                    &mut remote,
+                    &remote,
                     source_dir.clone(),
                     security_level,
                     &virtual_path,
@@ -149,7 +149,7 @@ mod tests {
                 .await?;
                 log::trace!(target: "citadel", "***CLIENT FILE TRANSFER SUCCESS***");
                 // now, pull it
-                let save_dir = crate::fs::read(&mut remote, virtual_path).await?;
+                let save_dir = crate::fs::read(&remote, virtual_path).await?;
                 // now, compare bytes
                 log::trace!(target: "citadel", "***CLIENT REVFS PULL SUCCESS");
                 let original_bytes = tokio::fs::read(&source_dir).await.unwrap();
@@ -206,12 +206,12 @@ mod tests {
             server_addr,
             UdpMode::Disabled,
             session_security_settings,
-            |_channel, mut remote| async move {
+            |_channel, remote| async move {
                 log::trace!(target: "citadel", "***CLIENT LOGIN SUCCESS :: File transfer next ***");
                 let virtual_path = PathBuf::from("/home/john.doe/TheBridge.pdf");
                 // write to file to the RE-VFS
                 crate::fs::write_with_security_level(
-                    &mut remote,
+                    &remote,
                     source_dir.clone(),
                     security_level,
                     &virtual_path,
@@ -219,7 +219,7 @@ mod tests {
                 .await?;
                 log::trace!(target: "citadel", "***CLIENT FILE TRANSFER SUCCESS***");
                 // now, pull it
-                let save_dir = crate::fs::take(&mut remote, &virtual_path).await?;
+                let save_dir = crate::fs::take(&remote, &virtual_path).await?;
                 // now, compare bytes
                 log::trace!(target: "citadel", "***CLIENT REVFS PULL SUCCESS");
                 let original_bytes = tokio::fs::read(&source_dir).await.unwrap();
@@ -227,7 +227,7 @@ mod tests {
                 assert_eq!(original_bytes, revfs_pulled_bytes);
                 log::trace!(target: "citadel", "***CLIENT REVFS PULL COMPARE SUCCESS");
                 // prove we can no longer read from this virtual file
-                assert!(crate::fs::read(&mut remote, &virtual_path).await.is_err());
+                assert!(crate::fs::read(&remote, &virtual_path).await.is_err());
                 client_success.store(true, Ordering::Relaxed);
                 remote.shutdown_kernel().await
             },
@@ -278,12 +278,12 @@ mod tests {
             server_addr,
             UdpMode::Disabled,
             session_security_settings,
-            |_channel, mut remote| async move {
+            |_channel, remote| async move {
                 log::trace!(target: "citadel", "***CLIENT LOGIN SUCCESS :: File transfer next ***");
                 let virtual_path = PathBuf::from("/home/john.doe/TheBridge.pdf");
                 // write to file to the RE-VFS
                 crate::fs::write_with_security_level(
-                    &mut remote,
+                    &remote,
                     source_dir.clone(),
                     security_level,
                     &virtual_path,
@@ -291,16 +291,16 @@ mod tests {
                 .await?;
                 log::trace!(target: "citadel", "***CLIENT FILE TRANSFER SUCCESS***");
                 // now, pull it
-                let save_dir = crate::fs::read(&mut remote, &virtual_path).await?;
+                let save_dir = crate::fs::read(&remote, &virtual_path).await?;
                 // now, compare bytes
                 log::trace!(target: "citadel", "***CLIENT REVFS PULL SUCCESS");
                 let original_bytes = tokio::fs::read(&source_dir).await.unwrap();
                 let revfs_pulled_bytes = tokio::fs::read(&save_dir).await.unwrap();
                 assert_eq!(original_bytes, revfs_pulled_bytes);
                 log::trace!(target: "citadel", "***CLIENT REVFS PULL COMPARE SUCCESS");
-                crate::fs::delete(&mut remote, &virtual_path).await?;
+                crate::fs::delete(&remote, &virtual_path).await?;
                 // prove we can no longer read from this virtual file since it was just deleted
-                assert!(crate::fs::read(&mut remote, &virtual_path).await.is_err());
+                assert!(crate::fs::read(&remote, &virtual_path).await.is_err());
                 client_success.store(true, Ordering::Relaxed);
                 remote.shutdown_kernel().await
             },
diff --git a/citadel_sdk/src/macros.rs b/citadel_sdk/src/macros.rs
index da36307b9..dad447af2 100644
--- a/citadel_sdk/src/macros.rs
+++ b/citadel_sdk/src/macros.rs
@@ -4,7 +4,7 @@ macro_rules! impl_remote {
         #[$crate::async_trait]
         impl Remote for $item {
             async fn send_with_custom_ticket(
-                &mut self,
+                &self,
                 ticket: Ticket,
                 request: NodeRequest,
             ) -> Result<(), NetworkError> {
@@ -12,7 +12,7 @@ macro_rules! impl_remote {
             }
 
             async fn send_callback_subscription(
-                &mut self,
+                &self,
                 request: NodeRequest,
             ) -> Result<
                 citadel_proto::kernel::kernel_communicator::KernelStreamSubscription,
@@ -22,7 +22,7 @@ macro_rules! impl_remote {
             }
 
             async fn send_callback(
-                &mut self,
+                &self,
                 request: NodeRequest,
             ) -> Result<NodeResult, NetworkError> {
                 self.inner.send_callback(request).await
diff --git a/citadel_sdk/src/prefabs/client/broadcast.rs b/citadel_sdk/src/prefabs/client/broadcast.rs
index 1d5911827..5ee8c8a2e 100644
--- a/citadel_sdk/src/prefabs/client/broadcast.rs
+++ b/citadel_sdk/src/prefabs/client/broadcast.rs
@@ -77,7 +77,7 @@ where
     )]
     async fn on_c2s_channel_received(
         connect_success: ConnectionSuccess,
-        mut remote: ClientServerRemote,
+        remote: ClientServerRemote,
         arg: GroupInitRequestType,
         fx: Self::UserLevelInputFunction,
         shared: Arc<BroadcastShared>,
@@ -136,7 +136,7 @@ where
                 let owner = if let Some(owner) = owner_find {
                     Some(owner)
                 } else if do_peer_register {
-                    let mut handle = remote
+                    let handle = remote
                         .inner
                         .propose_target(local_user.clone(), owner_orig.clone())
                         .await?;
@@ -172,7 +172,7 @@ where
         let acceptor_task = if creator_only_accept_inbound_registers {
             shared.route_registers.store(true, Ordering::Relaxed);
             let mut reg_rx = { shared.register_rx.lock().take().unwrap() };
-            let mut remote = remote.inner.clone();
+            let remote = remote.inner.clone();
             Box::pin(async move {
                 // Accept every inbound request, so long as the cid is equal to the
                 // cid for this group owner
@@ -187,7 +187,7 @@ where
 
                         log::trace!(target: "citadel", "Sending ACCEPT_REQUEST to {}", cid);
 
-                        let _ = responses::peer_register(reg_request, true, &mut remote).await?;
+                        let _ = responses::peer_register(reg_request, true, &remote).await?;
                     }
                 }
 
@@ -342,7 +342,7 @@ mod tests {
                 uuid,
                 server_addr,
                 request,
-                move |channel, mut remote| async move {
+                move |channel, remote| async move {
                     log::trace!(target: "citadel", "***GROUP PEER {}={} CONNECT SUCCESS***", idx,uuid);
                     let _ = client_success.fetch_add(1, Ordering::Relaxed);
                     wait_for_peers().await;
@@ -417,7 +417,7 @@ mod tests {
                 uuid,
                 server_addr,
                 peers,
-                move |mut results, mut remote| async move {
+                move |mut results, remote| async move {
                     let _implicated_cid = remote.conn_type.get_implicated_cid();
                     let mut signals = remote.get_unprocessed_signals_receiver().unwrap();
 
@@ -448,7 +448,7 @@ mod tests {
                                     let _ = crate::responses::group_invite(
                                         evt,
                                         true,
-                                        &mut remote.inner,
+                                        &remote.inner,
                                     )
                                     .await?;
                                 }
diff --git a/citadel_sdk/src/prefabs/client/peer_connection.rs b/citadel_sdk/src/prefabs/client/peer_connection.rs
index 2aa320dfa..91c306ce7 100644
--- a/citadel_sdk/src/prefabs/client/peer_connection.rs
+++ b/citadel_sdk/src/prefabs/client/peer_connection.rs
@@ -274,7 +274,7 @@ where
         {
             // each task will be responsible for possibly registering to and connecting
             // with the desired peer
-            let mut remote = remote.clone();
+            let remote = remote.clone();
             let PeerConnectionSettings {
                 id,
                 session_security_settings,
@@ -287,11 +287,11 @@ where
                     let (file_transfer_tx, file_transfer_rx) =
                         tokio::sync::mpsc::unbounded_channel();
 
-                    let mut handle = if let Some(_already_registered) = mutually_registered {
+                    let handle = if let Some(_already_registered) = mutually_registered {
                         remote.find_target(implicated_cid, id).await?
                     } else {
                         // TODO: optimize peer registration + connection in one go
-                        let mut handle = remote.propose_target(implicated_cid, id.clone()).await?;
+                        let handle = remote.propose_target(implicated_cid, id.clone()).await?;
 
                         // if the peer is not yet registered to the central node, wait for it to become registered
                         // this is useful especially for testing purposes
@@ -520,7 +520,7 @@ mod tests {
 
                     while let Some(conn) = results.recv().await {
                         log::trace!(target: "citadel", "User {} received {:?}", uuid, conn);
-                        let mut conn = conn?;
+                        let conn = conn?;
                         let peer_cid = conn.channel.get_peer_cid();
 
                         crate::test_common::p2p_assertions(implicated_cid, &conn).await;
@@ -614,7 +614,7 @@ mod tests {
                     while let Some(conn) = results.recv().await {
                         log::trace!(target: "citadel", "User {} received {:?}", uuid, conn);
                         wait_for_peers().await;
-                        let mut conn = conn?;
+                        let conn = conn?;
                         //let peer_cid = conn.channel.get_peer_cid();
 
                         crate::test_common::p2p_assertions(implicated_cid, &conn).await;
@@ -739,7 +739,7 @@ mod tests {
 
                     while let Some(conn) = results.recv().await {
                         log::trace!(target: "citadel", "User {} received {:?}", uuid, conn);
-                        let mut conn = conn?;
+                        let conn = conn?;
                         crate::test_common::p2p_assertions(implicated_cid, &conn).await;
 
                         if idx == 0 {
@@ -816,7 +816,7 @@ mod tests {
 
                     while let Some(conn) = results.recv().await {
                         log::trace!(target: "citadel", "User {} received {:?}", uuid, conn);
-                        let mut conn = conn?;
+                        let conn = conn?;
                         crate::test_common::p2p_assertions(implicated_cid, &conn).await;
                         conn.remote.disconnect().await?;
                         success += 1;
diff --git a/citadel_sdk/src/prefabs/client/single_connection.rs b/citadel_sdk/src/prefabs/client/single_connection.rs
index 9fc6146ab..d51ff4bbd 100644
--- a/citadel_sdk/src/prefabs/client/single_connection.rs
+++ b/citadel_sdk/src/prefabs/client/single_connection.rs
@@ -188,7 +188,7 @@ where
         tracing::instrument(target = "citadel", skip_all, err(Debug))
     )]
     async fn on_start(&self) -> Result<(), NetworkError> {
-        let mut remote = self.remote.clone().unwrap();
+        let remote = self.remote.clone().unwrap();
         let (auth_info, handler) = {
             (
                 self.auth_info.lock().take().unwrap(),
@@ -403,7 +403,7 @@ mod tests {
             server_addr,
             udp_mode,
             Default::default(),
-            |channel, mut remote| async move {
+            |channel, remote| async move {
                 log::trace!(target: "citadel", "***CLIENT TEST SUCCESS***");
                 wait_for_peers().await;
                 crate::test_common::udp_mode_assertions(udp_mode, channel.udp_channel_rx).await;
@@ -454,7 +454,7 @@ mod tests {
             server_addr,
             udp_mode,
             Default::default(),
-            |channel, mut remote| async move {
+            |channel, remote| async move {
                 log::trace!(target: "citadel", "***CLIENT TEST SUCCESS***");
                 wait_for_peers().await;
                 crate::test_common::udp_mode_assertions(udp_mode, channel.udp_channel_rx).await;
@@ -501,7 +501,7 @@ mod tests {
             server_addr,
             udp_mode,
             Default::default(),
-            |channel, mut remote| async move {
+            |channel, remote| async move {
                 log::trace!(target: "citadel", "***CLIENT TEST SUCCESS***");
                 wait_for_peers().await;
                 crate::test_common::udp_mode_assertions(udp_mode, channel.udp_channel_rx).await;
@@ -577,7 +577,7 @@ mod tests {
             server_addr,
             udp_mode,
             Default::default(),
-            |_channel, mut remote| async move {
+            |_channel, remote| async move {
                 log::trace!(target: "citadel", "***CLIENT TEST SUCCESS***");
                 wait_for_peers().await;
 
diff --git a/citadel_sdk/src/prefabs/mod.rs b/citadel_sdk/src/prefabs/mod.rs
index ad7466421..685ae6f23 100644
--- a/citadel_sdk/src/prefabs/mod.rs
+++ b/citadel_sdk/src/prefabs/mod.rs
@@ -46,8 +46,8 @@ impl TargetLockedRemote for ClientServerRemote {
     fn user(&self) -> &VirtualTargetType {
         &self.conn_type
     }
-    fn remote(&mut self) -> &mut NodeRemote {
-        &mut self.inner
+    fn remote(&self) -> &NodeRemote {
+        &self.inner
     }
     fn target_username(&self) -> Option<&String> {
         None
@@ -59,7 +59,7 @@ impl TargetLockedRemote for ClientServerRemote {
 
 impl ClientServerRemote {
     /// Gracefully closes the protocol and kernel executor
-    pub async fn shutdown_kernel(mut self) -> Result<(), NetworkError> {
+    pub async fn shutdown_kernel(self) -> Result<(), NetworkError> {
         self.inner.shutdown().await
     }
 
diff --git a/citadel_sdk/src/prefabs/shared/internal_service.rs b/citadel_sdk/src/prefabs/shared/internal_service.rs
index 364cdf899..d07619f3c 100644
--- a/citadel_sdk/src/prefabs/shared/internal_service.rs
+++ b/citadel_sdk/src/prefabs/shared/internal_service.rs
@@ -9,7 +9,7 @@ use std::task::{Context, Poll};
 use tokio::io::{AsyncRead, AsyncWrite, ReadBuf};
 
 pub async fn internal_service<F, Fut, R>(
-    mut remote: R,
+    remote: R,
     connect_success: ConnectionSuccess,
     service: F,
 ) -> Result<(), NetworkError>
diff --git a/citadel_sdk/src/remote_ext.rs b/citadel_sdk/src/remote_ext.rs
index 49589e28e..1e76c107d 100644
--- a/citadel_sdk/src/remote_ext.rs
+++ b/citadel_sdk/src/remote_ext.rs
@@ -12,13 +12,13 @@ use std::time::Duration;
 
 pub(crate) mod user_ids {
     use crate::prelude::*;
-    use std::ops::{Deref, DerefMut};
+    use std::ops::Deref;
 
     #[derive(Debug)]
     /// A reference to a user identifier
     pub struct SymmetricIdentifierHandleRef<'a> {
         pub(crate) user: VirtualTargetType,
-        pub(crate) remote: &'a mut NodeRemote,
+        pub(crate) remote: &'a NodeRemote,
         pub(crate) target_username: Option<String>,
     }
 
@@ -40,9 +40,9 @@ pub(crate) mod user_ids {
         target_username: Option<String>,
     }
 
-    pub trait TargetLockedRemote: Send {
+    pub trait TargetLockedRemote: Send + Sync {
         fn user(&self) -> &VirtualTargetType;
-        fn remote(&mut self) -> &mut NodeRemote;
+        fn remote(&self) -> &NodeRemote;
         fn target_username(&self) -> Option<&String>;
         fn user_mut(&mut self) -> &mut VirtualTargetType;
     }
@@ -51,7 +51,7 @@ pub(crate) mod user_ids {
         fn user(&self) -> &VirtualTargetType {
             &self.user
         }
-        fn remote(&mut self) -> &mut NodeRemote {
+        fn remote(&self) -> &NodeRemote {
             self.remote
         }
         fn target_username(&self) -> Option<&String> {
@@ -66,8 +66,8 @@ pub(crate) mod user_ids {
         fn user(&self) -> &VirtualTargetType {
             &self.user
         }
-        fn remote(&mut self) -> &mut NodeRemote {
-            &mut self.remote
+        fn remote(&self) -> &NodeRemote {
+            &self.remote
         }
         fn target_username(&self) -> Option<&String> {
             self.target_username.as_ref()
@@ -98,18 +98,6 @@ pub(crate) mod user_ids {
             self.remote
         }
     }
-
-    impl DerefMut for SymmetricIdentifierHandle {
-        fn deref_mut(&mut self) -> &mut Self::Target {
-            &mut self.remote
-        }
-    }
-
-    impl DerefMut for SymmetricIdentifierHandleRef<'_> {
-        fn deref_mut(&mut self) -> &mut Self::Target {
-            self.remote
-        }
-    }
 }
 
 /// Contains the elements required to communicate with the adjacent node
@@ -127,7 +115,7 @@ pub struct ConnectionSuccess {
 pub struct RegisterSuccess {}
 
 #[async_trait]
-/// Endows the [NodeRemote](crate::prelude::NodeRemote) with additional functions
+/// Endows the [NodeRemote](NodeRemote) with additional functions
 pub trait ProtocolRemoteExt: Remote {
     /// Registers with custom settings
     /// Returns a ticket which is used to uniquely identify the request in the protocol
@@ -137,7 +125,7 @@ pub trait ProtocolRemoteExt: Remote {
         V: Into<String> + Send,
         K: Into<SecBuffer> + Send,
     >(
-        &mut self,
+        &self,
         addr: T,
         full_name: R,
         username: V,
@@ -172,7 +160,7 @@ pub trait ProtocolRemoteExt: Remote {
         V: Into<String> + Send,
         K: Into<SecBuffer> + Send,
     >(
-        &mut self,
+        &self,
         addr: T,
         full_name: R,
         username: V,
@@ -191,7 +179,7 @@ pub trait ProtocolRemoteExt: Remote {
     /// Connects with custom settings
     /// Returns a ticket which is used to uniquely identify the request in the protocol
     async fn connect(
-        &mut self,
+        &self,
         auth: AuthenticationRequest,
         connect_mode: ConnectMode,
         udp_mode: UdpMode,
@@ -239,7 +227,7 @@ pub trait ProtocolRemoteExt: Remote {
     /// Connects with the default settings
     /// If FCM keys were created during the registration phase, then those keys will be used for the session. If new FCM keys need to be used, consider using [`Self::connect`]
     async fn connect_with_defaults(
-        &mut self,
+        &self,
         auth: AuthenticationRequest,
     ) -> Result<ConnectionSuccess, NetworkError> {
         self.connect(
@@ -262,7 +250,7 @@ pub trait ProtocolRemoteExt: Remote {
     /// # });
     /// ```
     async fn find_target<T: Into<UserIdentifier> + Send, R: Into<UserIdentifier> + Send>(
-        &mut self,
+        &self,
         local_user: T,
         peer: R,
     ) -> Result<SymmetricIdentifierHandleRef<'_>, NetworkError> {
@@ -278,7 +266,7 @@ pub trait ProtocolRemoteExt: Remote {
                             interserver_cid: peer.parent_icid,
                             peer_cid: peer.cid,
                         },
-                        remote: self.remote_ref_mut(),
+                        remote: self.remote_ref(),
                         target_username: None,
                     }
                 } else {
@@ -287,7 +275,7 @@ pub trait ProtocolRemoteExt: Remote {
                             implicated_cid: cid,
                             peer_cid: peer.cid,
                         },
-                        remote: self.remote_ref_mut(),
+                        remote: self.remote_ref(),
                         target_username: None,
                     }
                 }
@@ -298,7 +286,7 @@ pub trait ProtocolRemoteExt: Remote {
     /// Creates a proposed target from the valid local user to an unregistered peer in the network. Used when creating registration requests for peers.
     /// Currently only supports LocalGroup <-> LocalGroup peer connections
     async fn propose_target<T: Into<UserIdentifier> + Send, P: Into<UserIdentifier> + Send>(
-        &mut self,
+        &self,
         local_user: T,
         peer: P,
     ) -> Result<SymmetricIdentifierHandleRef<'_>, NetworkError> {
@@ -309,7 +297,7 @@ pub trait ProtocolRemoteExt: Remote {
                     implicated_cid: local_cid,
                     peer_cid,
                 },
-                remote: self.remote_ref_mut(),
+                remote: self.remote_ref(),
                 target_username: None,
             }),
             UserIdentifier::Username(uname) => Ok(SymmetricIdentifierHandleRef {
@@ -317,7 +305,7 @@ pub trait ProtocolRemoteExt: Remote {
                     implicated_cid: local_cid,
                     peer_cid: 0,
                 },
-                remote: self.remote_ref_mut(),
+                remote: self.remote_ref(),
                 target_username: Some(uname),
             }),
         }
@@ -326,7 +314,7 @@ pub trait ProtocolRemoteExt: Remote {
     /// Returns a list of local group peers on the network for local_user. May or may not be registered to the user. To get a list of registered users to local_user, run [`Self::get_local_group_mutual_peers`]
     /// - limit: if None, all peers are obtained. If Some, at most the specified number of peers will be obtained
     async fn get_local_group_peers<T: Into<UserIdentifier> + Send>(
-        &mut self,
+        &self,
         local_user: T,
         limit: Option<usize>,
     ) -> Result<Vec<LocalGroupPeer>, NetworkError> {
@@ -366,7 +354,7 @@ pub trait ProtocolRemoteExt: Remote {
 
     /// Returns a list of mutually-registered peers with the local_user
     async fn get_local_group_mutual_peers<T: Into<UserIdentifier> + Send>(
-        &mut self,
+        &self,
         local_user: T,
     ) -> Result<Vec<LocalGroupPeer>, NetworkError> {
         let local_cid = self.get_implicated_cid(local_user).await?;
@@ -399,11 +387,11 @@ pub trait ProtocolRemoteExt: Remote {
     }
 
     #[doc(hidden)]
-    fn remote_ref_mut(&mut self) -> &mut NodeRemote;
+    fn remote_ref(&self) -> &NodeRemote;
 
     #[doc(hidden)]
     async fn get_implicated_cid<T: Into<UserIdentifier> + Send>(
-        &mut self,
+        &self,
         local_user: T,
     ) -> Result<u64, NetworkError> {
         let account_manager = self.account_manager();
@@ -429,14 +417,14 @@ pub fn map_errors(result: NodeResult) -> Result<NodeResult, NetworkError> {
 }
 
 impl ProtocolRemoteExt for NodeRemote {
-    fn remote_ref_mut(&mut self) -> &mut NodeRemote {
+    fn remote_ref(&self) -> &NodeRemote {
         self
     }
 }
 
 impl ProtocolRemoteExt for ClientServerRemote {
-    fn remote_ref_mut(&mut self) -> &mut NodeRemote {
-        &mut self.inner
+    fn remote_ref(&self) -> &NodeRemote {
+        &self.inner
     }
 }
 
@@ -445,7 +433,7 @@ impl ProtocolRemoteExt for ClientServerRemote {
 pub trait ProtocolRemoteTargetExt: TargetLockedRemote {
     /// Sends a file with a custom size. The smaller the chunks, the higher the degree of scrambling, but the higher the performance cost. A chunk size of zero will use the default
     async fn send_file_with_custom_opts<T: ObjectSource>(
-        &mut self,
+        &self,
         source: T,
         chunk_size: usize,
         transfer_type: TransferType,
@@ -490,7 +478,7 @@ pub trait ProtocolRemoteTargetExt: TargetLockedRemote {
     }
 
     /// Sends a file to the provided target using the default chunking size
-    async fn send_file<T: ObjectSource>(&mut self, source: T) -> Result<(), NetworkError> {
+    async fn send_file<T: ObjectSource>(&self, source: T) -> Result<(), NetworkError> {
         self.send_file_with_custom_opts(source, 0, TransferType::FileTransfer)
             .await
     }
@@ -501,7 +489,7 @@ pub trait ProtocolRemoteTargetExt: TargetLockedRemote {
         T: ObjectSource,
         R: Into<PathBuf> + Send,
     >(
-        &mut self,
+        &self,
         source: T,
         virtual_directory: R,
         chunk_size: usize,
@@ -522,7 +510,7 @@ pub trait ProtocolRemoteTargetExt: TargetLockedRemote {
     /// Sends a file to the provided target using the default chunking size with local encryption.
     /// Only this local node may decrypt the information send to the adjacent node.
     async fn remote_encrypted_virtual_filesystem_push<T: ObjectSource, R: Into<PathBuf> + Send>(
-        &mut self,
+        &self,
         source: T,
         virtual_directory: R,
         security_level: SecurityLevel,
@@ -539,7 +527,7 @@ pub trait ProtocolRemoteTargetExt: TargetLockedRemote {
     /// Pulls a virtual file from the RE-VFS. If `delete_on_pull` is true, then, the virtual file
     /// will be taken from the RE-VFS
     async fn remote_encrypted_virtual_filesystem_pull<R: Into<PathBuf> + Send>(
-        &mut self,
+        &self,
         virtual_directory: R,
         transfer_security_level: SecurityLevel,
         delete_on_pull: bool,
@@ -587,7 +575,7 @@ pub trait ProtocolRemoteTargetExt: TargetLockedRemote {
     /// consider calling `Self::remote_encrypted_virtual_filesystem_pull` with the delete
     /// parameter set to true
     async fn remote_encrypted_virtual_filesystem_delete<R: Into<PathBuf> + Send>(
-        &mut self,
+        &self,
         virtual_directory: R,
     ) -> Result<(), NetworkError> {
         let request = NodeRequest::DeleteObject(DeleteObject {
@@ -610,7 +598,7 @@ pub trait ProtocolRemoteTargetExt: TargetLockedRemote {
 
     /// Connects to the peer with custom settings
     async fn connect_to_peer_custom(
-        &mut self,
+        &self,
         session_security_settings: SessionSecuritySettings,
         udp_mode: UdpMode,
     ) -> Result<PeerConnectSuccess, NetworkError> {
@@ -666,13 +654,13 @@ pub trait ProtocolRemoteTargetExt: TargetLockedRemote {
     }
 
     /// Connects to the target peer with default settings
-    async fn connect_to_peer(&mut self) -> Result<PeerConnectSuccess, NetworkError> {
+    async fn connect_to_peer(&self) -> Result<PeerConnectSuccess, NetworkError> {
         self.connect_to_peer_custom(Default::default(), Default::default())
             .await
     }
 
     /// Posts a registration request to a peer
-    async fn register_to_peer(&mut self) -> Result<PeerRegisterStatus, NetworkError> {
+    async fn register_to_peer(&self) -> Result<PeerRegisterStatus, NetworkError> {
         let implicated_cid = self.user().get_implicated_cid();
         let peer_target = self.try_as_peer_connection().await?;
         // TODO: Get rid of this step. Should be handled by the protocol
@@ -719,7 +707,7 @@ pub trait ProtocolRemoteTargetExt: TargetLockedRemote {
     /// Deregisters the currently locked target. If the target is a client to server
     /// connection, deregisters from the server. If the target is a p2p connection,
     /// deregisters the p2p
-    async fn deregister(&mut self) -> Result<(), NetworkError> {
+    async fn deregister(&self) -> Result<(), NetworkError> {
         if let Ok(peer_conn) = self.try_as_peer_connection().await {
             let peer_request = PeerSignal::Deregister(peer_conn);
             let implicated_cid = self.user().get_implicated_cid();
@@ -771,7 +759,7 @@ pub trait ProtocolRemoteTargetExt: TargetLockedRemote {
         Err(NetworkError::InternalError("Deregister ended unexpectedly"))
     }
 
-    async fn disconnect(&mut self) -> Result<(), NetworkError> {
+    async fn disconnect(&self) -> Result<(), NetworkError> {
         if let Ok(peer_conn) = self.try_as_peer_connection().await {
             if let PeerConnectionType::LocalGroupPeer {
                 implicated_cid,
@@ -832,7 +820,7 @@ pub trait ProtocolRemoteTargetExt: TargetLockedRemote {
     }
 
     async fn create_group(
-        &mut self,
+        &self,
         initial_users_to_invite: Option<Vec<UserIdentifier>>,
     ) -> Result<GroupChannel, NetworkError> {
         let implicated_cid = self.user().get_implicated_cid();
@@ -881,7 +869,7 @@ pub trait ProtocolRemoteTargetExt: TargetLockedRemote {
     }
 
     /// Lists all groups that which the current peer owns
-    async fn list_owned_groups(&mut self) -> Result<Vec<MessageGroupKey>, NetworkError> {
+    async fn list_owned_groups(&self) -> Result<Vec<MessageGroupKey>, NetworkError> {
         let implicated_cid = self.user().get_implicated_cid();
         let group_request = GroupBroadcast::ListGroupsFor(implicated_cid);
         let request = NodeRequest::GroupBroadcastCommand(GroupBroadcastCommand {
@@ -909,7 +897,7 @@ pub trait ProtocolRemoteTargetExt: TargetLockedRemote {
     /// Begins a re-key, updating the container in the process.
     /// Returns the new key matrix version. Does not return the new key version
     /// if the rekey fails, or, if a current rekey is already executing
-    async fn rekey(&mut self) -> Result<Option<u32>, NetworkError> {
+    async fn rekey(&self) -> Result<Option<u32>, NetworkError> {
         let request = NodeRequest::ReKey(ReKey {
             v_conn_type: *self.user(),
         });
@@ -933,7 +921,7 @@ pub trait ProtocolRemoteTargetExt: TargetLockedRemote {
     }
 
     /// Checks if the locked target is registered
-    async fn is_peer_registered(&mut self) -> Result<bool, NetworkError> {
+    async fn is_peer_registered(&self) -> Result<bool, NetworkError> {
         let target = self.try_as_peer_connection().await?;
         if let PeerConnectionType::LocalGroupPeer {
             implicated_cid: local_cid,
@@ -951,7 +939,7 @@ pub trait ProtocolRemoteTargetExt: TargetLockedRemote {
     }
 
     #[doc(hidden)]
-    async fn try_as_peer_connection(&mut self) -> Result<PeerConnectionType, NetworkError> {
+    async fn try_as_peer_connection(&self) -> Result<PeerConnectionType, NetworkError> {
         let verified_return = |user: &VirtualTargetType| {
             user.try_as_peer_connection()
                 .ok_or(NetworkError::InvalidRequest("Target is not a peer"))
@@ -981,8 +969,9 @@ pub trait ProtocolRemoteTargetExt: TargetLockedRemote {
                 .map(|r| r.1.cid)
                 .unwrap_or(expected_peer_cid);
 
-            self.user_mut().set_target_cid(peer_cid);
-            verified_return(self.user())
+            let mut user = *self.user();
+            user.set_target_cid(peer_cid);
+            verified_return(&user)
         } else {
             verified_return(self.user())
         }
@@ -1049,8 +1038,8 @@ pub mod remote_specialization {
         fn user(&self) -> &VirtualTargetType {
             &self.peer
         }
-        fn remote(&mut self) -> &mut NodeRemote {
-            &mut self.inner
+        fn remote(&self) -> &NodeRemote {
+            &self.inner
         }
         fn target_username(&self) -> Option<&String> {
             self.username.as_ref()
@@ -1186,7 +1175,7 @@ mod tests {
             server_addr,
             UdpMode::Disabled,
             session_security_settings,
-            |_channel, mut remote| async move {
+            |_channel, remote| async move {
                 log::trace!(target: "citadel", "***CLIENT LOGIN SUCCESS :: File transfer next ***");
                 remote
                     .send_file_with_custom_opts(
diff --git a/citadel_sdk/src/responses.rs b/citadel_sdk/src/responses.rs
index a2eb3c14f..4dd51e784 100644
--- a/citadel_sdk/src/responses.rs
+++ b/citadel_sdk/src/responses.rs
@@ -12,7 +12,7 @@ use crate::prelude::*;
 pub async fn peer_register(
     input_signal: PeerSignal,
     accept: bool,
-    remote: &mut impl Remote,
+    remote: &impl Remote,
 ) -> Result<Ticket, NetworkError> {
     if let PeerSignal::PostRegister(v_conn, username, username_opt, ticket, None) = input_signal {
         let this_cid = v_conn.get_original_target_cid();
@@ -60,7 +60,7 @@ pub async fn peer_register(
 pub async fn peer_connect(
     input_signal: PeerSignal,
     accept: bool,
-    remote: &mut impl Remote,
+    remote: &impl Remote,
 ) -> Result<Ticket, NetworkError> {
     if let PeerSignal::PostConnect(v_conn, ticket, None, sess_sec, udp_mode) = input_signal {
         let this_cid = v_conn.get_original_target_cid();
@@ -97,7 +97,7 @@ pub async fn peer_connect(
 pub async fn group_invite(
     invite_signal: NodeResult,
     accept: bool,
-    remote: &mut impl Remote,
+    remote: &impl Remote,
 ) -> Result<Ticket, NetworkError> {
     if let NodeResult::GroupEvent(GroupEvent {
         implicated_cid: cid,
diff --git a/citadel_user/src/account_manager.rs b/citadel_user/src/account_manager.rs
index af2fa03eb..21c74655c 100644
--- a/citadel_user/src/account_manager.rs
+++ b/citadel_user/src/account_manager.rs
@@ -111,6 +111,11 @@ impl<R: Ratchet, Fcm: Ratchet> AccountManager<R, Fcm> {
         let auth_store = creds
             .derive_server_container(&self.node_argon_settings, self.get_misc_settings())
             .await?;
+
+        self.server_misc_settings
+            .credential_requirements
+            .check::<_, &str, _>(auth_store.username(), None, auth_store.full_name())?;
+
         let pers = &self.persistence_handler;
 
         // We must lock the config to ensure that the obtained CID gets added into the database before any competing threads may get called
diff --git a/citadel_user/src/backend/mysql_backend.rs b/citadel_user/src/backend/mysql_backend.rs
index 85373246d..37d48f603 100644
--- a/citadel_user/src/backend/mysql_backend.rs
+++ b/citadel_user/src/backend/mysql_backend.rs
@@ -3,7 +3,8 @@ use crate::backend::memory::no_backend_streaming;
 use crate::backend::utils::ObjectTransferStatus;
 use crate::backend::{BackendConnection, BackendType};
 use crate::client_account::{ClientNetworkAccount, MutualPeer};
-use crate::misc::{AccountError, CNACMetadata, MAX_USERNAME_LENGTH};
+use crate::credentials::MAX_USERNAME_LENGTH;
+use crate::misc::{AccountError, CNACMetadata};
 use crate::prelude::{ClientNetworkAccountInner, HYPERLAN_IDX};
 use crate::serialization::SyncIO;
 use async_trait::async_trait;
diff --git a/citadel_user/src/client_account.rs b/citadel_user/src/client_account.rs
index 345925d45..ca2d6375f 100644
--- a/citadel_user/src/client_account.rs
+++ b/citadel_user/src/client_account.rs
@@ -1,9 +1,7 @@
 use serde::{Deserialize, Serialize};
 use std::sync::Arc;
 
-use crate::misc::{
-    check_credential_formatting, get_present_formatted_timestamp, AccountError, CNACMetadata,
-};
+use crate::misc::{get_present_formatted_timestamp, AccountError, CNACMetadata};
 use crate::prelude::ConnectionInfo;
 use multimap::MultiMap;
 
@@ -116,12 +114,6 @@ impl<R: Ratchet, Fcm: Ratchet> ClientNetworkAccount<R, Fcm> {
         base_hyper_ratchet: R,
     ) -> Result<Self, AccountError> {
         log::trace!(target: "citadel", "Creating CNAC w/valid cid: {:?}", valid_cid);
-        // TODO: move this to validation in citadel_proto (or this may be redundant)
-        check_credential_formatting::<_, &str, _>(
-            auth_store.username(),
-            None,
-            auth_store.full_name(),
-        )?;
         let creation_date = get_present_formatted_timestamp();
         let crypt_container = PeerSessionCrypto::<R>::new(
             Toolset::<R>::new(valid_cid, base_hyper_ratchet),
diff --git a/citadel_user/src/credentials.rs b/citadel_user/src/credentials.rs
new file mode 100644
index 000000000..82bb60090
--- /dev/null
+++ b/citadel_user/src/credentials.rs
@@ -0,0 +1,98 @@
+use crate::misc::AccountError;
+use serde::{Deserialize, Serialize};
+
+#[derive(Serialize, Deserialize, Clone)]
+pub struct CredentialRequirements {
+    pub min_password_length: u8,
+    pub max_password_length: u8,
+    pub min_username_length: u8,
+    pub max_username_length: u8,
+    pub min_name_length: u8,
+    pub max_name_length: u8,
+}
+
+impl Default for CredentialRequirements {
+    fn default() -> Self {
+        Self {
+            min_password_length: MIN_PASSWORD_LENGTH,
+            max_password_length: MAX_PASSWORD_LENGTH,
+            min_username_length: MIN_USERNAME_LENGTH,
+            max_username_length: MAX_USERNAME_LENGTH,
+            min_name_length: MIN_NAME_LENGTH,
+            max_name_length: MAX_NAME_LENGTH,
+        }
+    }
+}
+
+impl CredentialRequirements {
+    /// Used to determine if the desired credentials have a valid format, length, etc. This alone DOES NOT imply whether or not the
+    /// credentials are available
+    pub fn check<T: AsRef<str>, R: AsRef<str>, V: AsRef<str>>(
+        &self,
+        username: T,
+        password: Option<R>,
+        full_name: V,
+    ) -> Result<(), AccountError> {
+        let username = username.as_ref();
+        let full_name = full_name.as_ref();
+
+        if username.len() < self.min_username_length as _
+            || username.len() > self.max_username_length as _
+        {
+            return Err(AccountError::Generic(format!(
+                "Username must be between {} and {} characters",
+                self.min_username_length, self.max_username_length
+            )));
+        }
+
+        if username.contains(' ') {
+            return Err(AccountError::Generic(
+                "Username cannot contain spaces. Use a period instead".to_string(),
+            ));
+        }
+
+        if let Some(password) = password.as_ref() {
+            let password = password.as_ref();
+            if password.len() < self.min_password_length as _
+                || password.len() > self.max_password_length as _
+            {
+                return Err(AccountError::Generic(format!(
+                    "Password must be between {} and {} characters",
+                    self.min_password_length, self.max_password_length
+                )));
+            }
+
+            if password.contains(' ') {
+                return Err(AccountError::Generic(
+                    "Password cannot contain spaces".to_string(),
+                ));
+            }
+        }
+
+        if full_name.len() < self.min_name_length as _
+            || full_name.len() > self.max_name_length as _
+        {
+            return Err(AccountError::Generic(format!(
+                "Full name must be between {} and {} characters",
+                self.min_name_length, self.max_name_length
+            )));
+        }
+
+        Ok(())
+    }
+}
+
+///
+pub const MIN_PASSWORD_LENGTH: u8 = 7;
+///
+pub const MAX_PASSWORD_LENGTH: u8 = 17;
+
+///
+pub const MIN_USERNAME_LENGTH: u8 = 3;
+///
+pub const MAX_USERNAME_LENGTH: u8 = 37;
+
+///
+pub const MIN_NAME_LENGTH: u8 = 2;
+///
+pub const MAX_NAME_LENGTH: u8 = 77;
diff --git a/citadel_user/src/lib.rs b/citadel_user/src/lib.rs
index 5bc29a4cb..89c01b193 100644
--- a/citadel_user/src/lib.rs
+++ b/citadel_user/src/lib.rs
@@ -57,6 +57,7 @@ pub mod account_manager;
 pub mod auth;
 /// For handling different I/O operations
 pub mod backend;
+pub mod credentials;
 #[cfg(all(feature = "filesystem", not(target_family = "wasm")))]
 /// Environmental constants and subroutines for pre-checking the system
 pub mod directory_store;
diff --git a/citadel_user/src/misc.rs b/citadel_user/src/misc.rs
index d8c93a236..e500f9e23 100644
--- a/citadel_user/src/misc.rs
+++ b/citadel_user/src/misc.rs
@@ -51,67 +51,6 @@ impl<T: ToString> From<T> for AccountError {
     }
 }
 
-///
-pub const MIN_PASSWORD_LENGTH: usize = 7;
-///
-pub const MAX_PASSWORD_LENGTH: usize = 17;
-
-///
-pub const MIN_USERNAME_LENGTH: usize = 3;
-///
-pub const MAX_USERNAME_LENGTH: usize = 37;
-
-///
-pub const MIN_NAME_LENGTH: usize = 2;
-///
-pub const MAX_NAME_LENGTH: usize = 77;
-
-/// Used to determine if the desired credentials have a valid format, length, etc. This alone DOES NOT imply whether or not the
-/// credentials are available
-pub fn check_credential_formatting<T: AsRef<str>, R: AsRef<str>, V: AsRef<str>>(
-    username: T,
-    password: Option<R>,
-    full_name: V,
-) -> Result<(), AccountError> {
-    let username = username.as_ref();
-    let full_name = full_name.as_ref();
-
-    if username.len() < MIN_USERNAME_LENGTH || username.len() > MAX_USERNAME_LENGTH {
-        return Err(AccountError::Generic(format!(
-            "Username must be between {MIN_USERNAME_LENGTH} and {MAX_USERNAME_LENGTH} characters",
-        )));
-    }
-
-    if username.contains(' ') {
-        return Err(AccountError::Generic(
-            "Username cannot contain spaces. Use a period instead".to_string(),
-        ));
-    }
-
-    if let Some(password) = password.as_ref() {
-        let password = password.as_ref();
-        if password.len() < MIN_PASSWORD_LENGTH || password.len() > MAX_PASSWORD_LENGTH {
-            return Err(AccountError::Generic(format!(
-                "Password must be between {MIN_PASSWORD_LENGTH} and {MAX_PASSWORD_LENGTH} characters",
-            )));
-        }
-
-        if password.contains(' ') {
-            return Err(AccountError::Generic(
-                "Password cannot contain spaces".to_string(),
-            ));
-        }
-    }
-
-    if full_name.len() < MIN_NAME_LENGTH || full_name.len() > MAX_NAME_LENGTH {
-        return Err(AccountError::Generic(format!(
-            "Full name must be between {MIN_NAME_LENGTH} and {MAX_NAME_LENGTH} characters",
-        )));
-    }
-
-    Ok(())
-}
-
 /// For passing metadata from a cnac
 #[derive(Debug)]
 pub struct CNACMetadata {
@@ -187,6 +126,13 @@ pub fn validate_virtual_path<R: AsRef<Path>>(virtual_path: R) -> Result<(), Acco
         )));
     }
 
+    if buf.contains("..") {
+        // we don't want the user trying to access files outside of the base directory
+        return Err(AccountError::IoError(format!(
+            "Path {virtual_path:?} cannot contain '..' for security reasons"
+        )));
+    }
+
     Ok(())
 }
 
@@ -236,6 +182,7 @@ mod tests {
     #[case("tmp.txt")]
     #[case("\\hello\\")]
     #[case("\\")]
+    #[case("/hello/world/../tmp.txt")]
     fn test_virtual_dir_formatting_bad(#[case] bad_path: &str) {
         let virtual_dir = PathBuf::from(bad_path);
         let formatted = prepare_virtual_path(virtual_dir);
diff --git a/citadel_user/src/server_misc_settings.rs b/citadel_user/src/server_misc_settings.rs
index 77bf3d596..6b645e6ba 100644
--- a/citadel_user/src/server_misc_settings.rs
+++ b/citadel_user/src/server_misc_settings.rs
@@ -1,14 +1,19 @@
+use crate::credentials::CredentialRequirements;
+
 /// Miscellaneous settings for a node serving connections
 #[derive(Clone)]
 pub struct ServerMiscSettings {
     /// If enabled, allows inbound connections to use no credentials when logging-in
     pub allow_passwordless: bool,
+    /// Enforces specific requirements on credentials
+    pub credential_requirements: CredentialRequirements,
 }
 
 impl Default for ServerMiscSettings {
     fn default() -> Self {
         Self {
             allow_passwordless: true,
+            credential_requirements: Default::default(),
         }
     }
 }
diff --git a/citadel_wire/src/udp_traversal/linear/encrypted_config_container.rs b/citadel_wire/src/udp_traversal/linear/encrypted_config_container.rs
index 5f86abba5..a1122e3cd 100644
--- a/citadel_wire/src/udp_traversal/linear/encrypted_config_container.rs
+++ b/citadel_wire/src/udp_traversal/linear/encrypted_config_container.rs
@@ -36,9 +36,10 @@ impl HolePunchConfigContainer {
         _decrypt_packet: impl Fn(&[u8]) -> Option<BytesMut> + Send + Sync + 'static,
         stun_servers: Option<Vec<String>>,
     ) -> Self {
-        let mut this: Self = Default::default();
-        this.stun_servers = stun_servers;
-        this
+        Self {
+            stun_servers,
+            ..Default::default()
+        }
     }
 
     /// Generates a packet
diff --git a/citadel_wire/src/udp_traversal/linear/method1.rs b/citadel_wire/src/udp_traversal/linear/method1.rs
deleted file mode 100644
index ab71ec134..000000000
--- a/citadel_wire/src/udp_traversal/linear/method1.rs
+++ /dev/null
@@ -1,12 +0,0 @@
-/*use std::net::SocketAddr;
-use crate::udp_traversal::RelativeNodeType;
-use citadel_io::UdpSocket;
-
-/// Method one: "Each Side Sends a packet with a long TTL"
-pub struct MethodOne {
-    stage: usize,
-    this_node_type: RelativeNodeType,
-    socket: UdpSocket,
-    target: Option<SocketAddr>
-}
- */
diff --git a/citadel_wire/src/udp_traversal/linear/method3.rs b/citadel_wire/src/udp_traversal/linear/method3.rs
index 6797e4e64..0b8f095d9 100644
--- a/citadel_wire/src/udp_traversal/linear/method3.rs
+++ b/citadel_wire/src/udp_traversal/linear/method3.rs
@@ -205,7 +205,7 @@ impl Method3 {
                         }
                     }
                     Err(err) => {
-                        log::warn!(target: "citadel", "Error sending packet from {:?}: {:?}", socket.socket.local_addr()?, err);
+                        log::warn!(target: "citadel", "Error sending packet from {:?} to {endpoint}: {:?}", socket.socket.local_addr()?, err);
                     }
                 }
             }
diff --git a/citadel_wire/src/udp_traversal/linear/mod.rs b/citadel_wire/src/udp_traversal/linear/mod.rs
index ff710a49f..c1ce0da0d 100644
--- a/citadel_wire/src/udp_traversal/linear/mod.rs
+++ b/citadel_wire/src/udp_traversal/linear/mod.rs
@@ -15,7 +15,6 @@ use netbeam::sync::RelativeNodeType;
 
 pub mod encrypted_config_container;
 
-pub mod method1;
 pub mod method3;
 
 /// Whereas UDP hole punching usually entails the connection between two peers (p2p),


Test patch:
diff --git a/citadel_sdk/tests/stress_tests.rs b/citadel_sdk/tests/stress_tests.rs
index d072b65fa..eafae365f 100644
--- a/citadel_sdk/tests/stress_tests.rs
+++ b/citadel_sdk/tests/stress_tests.rs
@@ -59,16 +59,14 @@ mod tests {
         }
 
         #[cfg(not(feature = "multi-threaded"))]
-        pub fn local_set(self) -> impl Future<Output = Result<(), JoinError>> {
-            async move {
-                self.local_set.await;
-                Ok(())
-            }
+        pub async fn local_set(self) -> Result<(), JoinError> {
+            self.local_set.await;
+            Ok(())
         }
 
         #[cfg(feature = "multi-threaded")]
-        pub fn local_set(self) -> impl Future<Output = Result<(), JoinError>> {
-            async move { Ok(()) }
+        pub async fn local_set(self) -> Result<(), JoinError> {
+            Ok(())
         }
     }
 
diff --git a/citadel_user/tests/primary.rs b/citadel_user/tests/primary.rs
index 5aa3cfc34..e3c9098e0 100644
--- a/citadel_user/tests/primary.rs
+++ b/citadel_user/tests/primary.rs
@@ -15,6 +15,7 @@ mod tests {
     use std::str::FromStr;
 
     use citadel_pqcrypto::prelude::algorithm_dictionary::EncryptionAlgorithm;
+    use citadel_user::credentials::CredentialRequirements;
     use citadel_user::misc::{AccountError, CNACMetadata};
     use citadel_user::prelude::{ConnectionInfo, MutualPeer};
     use std::collections::HashMap;
@@ -1472,53 +1473,71 @@ mod tests {
     #[test]
     fn test_credential_formatting() {
         // test below the username length
-        const MIN_USERNAME_LEN: usize = citadel_user::misc::MIN_USERNAME_LENGTH;
-        const MAX_USERNAME_LEN: usize = citadel_user::misc::MAX_USERNAME_LENGTH;
-        const MIN_PASSWORD_LEN: usize = citadel_user::misc::MIN_PASSWORD_LENGTH;
-        const MAX_PASSWORD_LEN: usize = citadel_user::misc::MAX_PASSWORD_LENGTH;
-        const MIN_NAME_LEN: usize = citadel_user::misc::MIN_NAME_LENGTH;
-        const MAX_NAME_LEN: usize = citadel_user::misc::MAX_NAME_LENGTH;
-
-        let good_username = &(0..(MIN_USERNAME_LEN + 1))
+        let requirements = CredentialRequirements::default();
+        let min_username_length = requirements.min_username_length;
+        let max_username_length = requirements.max_username_length;
+        let min_password_length = requirements.min_password_length;
+        let max_password_length = requirements.max_password_length;
+        let min_name_length = requirements.min_name_length;
+        let max_name_length = requirements.max_name_length;
+
+        let good_username = &(0..(min_username_length + 1))
             .map(|r| r.to_string())
             .collect::<String>();
-        let good_password = &(0..(MIN_PASSWORD_LEN + 1))
+        let good_password = &(0..(min_password_length + 1))
             .map(|r| r.to_string())
             .collect::<String>();
-        let good_name = &(0..(MIN_NAME_LEN + 1))
+        let good_name = &(0..(min_name_length + 1))
             .map(|r| r.to_string())
             .collect::<String>();
 
         fn generate_test(
-            min: usize,
-            max: usize,
+            requirements: &CredentialRequirements,
+            min: u8,
+            max: u8,
             ty: &str,
             good_username: &str,
             good_password: &str,
             good_name: &str,
         ) {
-            use citadel_user::misc::check_credential_formatting as check;
-
             let bad_below = &(0..(min - 1)).map(|r| r.to_string()).collect::<String>();
             let bad_above = &(0..(max + 1)).map(|r| r.to_string()).collect::<String>();
 
             match ty {
                 "username" => {
-                    assert!(check(bad_below, Some(good_password), good_name).is_err());
-                    assert!(check(good_username, Some(good_password), good_name).is_ok());
-                    assert!(check(bad_above, Some(good_password), good_name).is_err());
+                    assert!(requirements
+                        .check(bad_below, Some(good_password), good_name)
+                        .is_err());
+                    assert!(requirements
+                        .check(good_username, Some(good_password), good_name)
+                        .is_ok());
+                    assert!(requirements
+                        .check(bad_above, Some(good_password), good_name)
+                        .is_err());
                 }
 
                 "password" => {
-                    assert!(check(good_username, Some(bad_below), good_name).is_err());
-                    assert!(check(good_username, Some(good_password), good_name).is_ok());
-                    assert!(check(good_username, Some(bad_above), good_name).is_err());
+                    assert!(requirements
+                        .check(good_username, Some(bad_below), good_name)
+                        .is_err());
+                    assert!(requirements
+                        .check(good_username, Some(good_password), good_name)
+                        .is_ok());
+                    assert!(requirements
+                        .check(good_username, Some(bad_above), good_name)
+                        .is_err());
                 }
 
                 "name" => {
-                    assert!(check(good_username, Some(good_password), bad_below).is_err());
-                    assert!(check(good_username, Some(good_password), good_name).is_ok());
-                    assert!(check(good_username, Some(good_password), bad_above).is_err());
+                    assert!(requirements
+                        .check(good_username, Some(good_password), bad_below)
+                        .is_err());
+                    assert!(requirements
+                        .check(good_username, Some(good_password), good_name)
+                        .is_ok());
+                    assert!(requirements
+                        .check(good_username, Some(good_password), bad_above)
+                        .is_err());
                 }
 
                 _ => unreachable!("Bad unit test"),
@@ -1526,24 +1545,27 @@ mod tests {
         }
 
         generate_test(
-            MIN_USERNAME_LEN,
-            MAX_USERNAME_LEN,
+            &requirements,
+            min_username_length,
+            max_username_length,
             "username",
             good_username,
             good_password,
             good_name,
         );
         generate_test(
-            MIN_PASSWORD_LEN,
-            MAX_PASSWORD_LEN,
+            &requirements,
+            min_password_length,
+            max_password_length,
             "password",
             good_username,
             good_password,
             good_name,
         );
         generate_test(
-            MIN_NAME_LEN,
-            MAX_NAME_LEN,
+            &requirements,
+            min_name_length,
+            max_name_length,
             "name",
             good_username,
             good_password,