diff --git a/sshproto/src/auth.rs b/sshproto/src/auth.rs
index 03e2dd91f2ddac5fe8537d670e1ee8811f621c94..5646f3ebc78b054c7fd7685930ed7c301c3adc75 100644
--- a/sshproto/src/auth.rs
+++ b/sshproto/src/auth.rs
@@ -10,7 +10,6 @@ use heapless::{String, Vec};
 use crate::*;
 use behaviour::CliBehaviour;
 use client::*;
-use conn::RespPackets;
 use packets::ParseContext;
 use packets::{Packet, Signature, Userauth60};
 use sign::SignKey;
diff --git a/sshproto/src/behaviour.rs b/sshproto/src/behaviour.rs
index ebbe27eccfc11cca96e61c7d979c2365cf341a77..75f67af23292a6b54f209c5ba905f83f525cf6c9 100644
--- a/sshproto/src/behaviour.rs
+++ b/sshproto/src/behaviour.rs
@@ -6,7 +6,7 @@ use {
 
 use snafu::prelude::*;
 
-use crate::{*, conn::RespPackets};
+use crate::*;
 use packets::{ForwardedTcpip,DirectTcpip};
 use channel::ChanOpened;
 use sshnames::*;
diff --git a/sshproto/src/channel.rs b/sshproto/src/channel.rs
index f116e1226bfa2b6582eae6f8ac946569732d1a66..31ae28a450d6e810e6733b1f726b3ab4f26652a6 100644
--- a/sshproto/src/channel.rs
+++ b/sshproto/src/channel.rs
@@ -8,9 +8,10 @@ use core::mem;
 
 use heapless::{Deque, String, Vec};
 
-use crate::{conn::RespPackets, *, packets::ChannelOpenFailure};
+use crate::*;
 use config::*;
-use packets::{ChannelReqType, ChannelRequest, Packet, ChannelOpen, ChannelOpenType, ChannelData, ChannelDataExt};
+use packets::{ChannelReqType, ChannelOpenFailure, ChannelRequest, Packet, ChannelOpen, ChannelOpenType, ChannelData, ChannelDataExt};
+use traffic::TrafSend;
 use sshwire::{BinString, TextString};
 use sshnames::*;
 
@@ -196,20 +197,18 @@ impl Channels {
     }
 
     fn dispatch_open(&mut self, p: &ChannelOpen<'_>,
-        resp: &mut RespPackets<'_>,
+        s: &TrafSend,
         b: &mut Behaviour<'_>,
         ) -> Result<()> {
 
-        match self.dispatch_open_inner(p, resp, b) {
+        match self.dispatch_open_inner(p, s, b) {
             Err(DispatchOpenError::Failure(f)) => {
-                let r = packets::ChannelOpenFailure {
+                s.send(packets::ChannelOpenFailure {
                     num: p.num,
                     reason: f as u32,
                     desc: "".into(),
                     lang: "",
-                };
-                let r: Packet = r.into();
-                resp.push(r.into()).trap()?;
+                })?;
                 Ok(())
             },
             Err(DispatchOpenError::Error(e)) => Err(e),
@@ -219,7 +218,7 @@ impl Channels {
 
     // the caller will send failure messages if required
     fn dispatch_open_inner(&mut self, p: &ChannelOpen<'_>,
-        resp: &mut RespPackets<'_>,
+        s: &TrafSend,
         b: &mut Behaviour<'_>,
         ) -> Result<(), DispatchOpenError> {
 
@@ -261,7 +260,7 @@ impl Channels {
 
         match r {
             ChanOpened::Success => {
-                resp.push(ch.open_done().into()).trap()?
+                s.send(ch.open_done())?;
             },
             ChanOpened::Failure(f) => {
                 let n = ch.recv.num;
@@ -278,8 +277,8 @@ impl Channels {
 
     pub fn dispatch_request(&mut self,
         p: &packets::ChannelRequest,
-        resp: &mut RespPackets<'_>,
-        b: &mut Behaviour<'_>,
+        _s: &TrafSend,
+        _b: &mut Behaviour<'_>,
         ) -> Result<()> {
             let ch = match self.get(p.num) {
                 Ok(ch) => ch,
@@ -300,13 +299,13 @@ impl Channels {
     pub async fn dispatch(
         &mut self,
         packet: Packet<'_>,
-        resp: &mut RespPackets<'_>,
+        s: &TrafSend<'_>,
         b: &mut Behaviour<'_>,
     ) -> Result<Option<ChanEventMaker>> {
         trace!("chan dispatch");
         let r = match packet {
             Packet::ChannelOpen(p) => {
-                self.dispatch_open(&p, resp, b)?;
+                self.dispatch_open(&p, s, b)?;
                 Ok(None)
             }
 
@@ -324,7 +323,7 @@ impl Channels {
                                 window: p.initial_window as usize,
                             });
                             for r in init_req {
-                                ch.request(r, resp)?
+                                ch.request(r, s)?
                             }
                             ch.state = ChanState::Normal;
                         }
@@ -381,7 +380,7 @@ impl Channels {
                 Ok(None)
             }
             Packet::ChannelRequest(p) => {
-                self.dispatch_request(&p, resp, b)?;
+                self.dispatch_request(&p, s, b)?;
                 Ok(None)
             }
             Packet::ChannelSuccess(_p) => {
@@ -577,11 +576,10 @@ impl Channel {
         }
     }
 
-    fn request(&mut self, req: ReqDetails, resp: &mut RespPackets) -> Result<()> {
+    fn request(&mut self, req: ReqDetails, s: &TrafSend) -> Result<()> {
         let num = self.send.as_ref().trap()?.num;
         let r = Req { num, details: req };
-        resp.push(r.into()).trap()?;
-        Ok(())
+        s.send(r.packet()?)
     }
 
     pub(crate) fn number(&self) -> u32 {
diff --git a/sshproto/src/cliauth.rs b/sshproto/src/cliauth.rs
index cd517ab2c0b3dba17dbd6a7933956706b297ad94..b9a66083778756e761eb600722ca413fbadd0e9e 100644
--- a/sshproto/src/cliauth.rs
+++ b/sshproto/src/cliauth.rs
@@ -12,8 +12,8 @@ use pretty_hex::PrettyHex;
 
 use crate::{packets::UserauthPkOk, *};
 use behaviour::CliBehaviour;
+use traffic::TrafSend;
 use client::*;
-use conn::RespPackets;
 use packets::{MessageNumber, AuthMethod, MethodPubKey, ParseContext, UserauthRequest};
 use packets::{Packet, Signature, Userauth60};
 use sign::{SignKey, OwnedSig};
@@ -96,24 +96,22 @@ impl CliAuth {
     // May be called multiple times
     pub async fn start<'b>(
         &'b mut self,
-        resp: &mut RespPackets<'b>,
+        s: &TrafSend<'_>,
         b: &mut dyn CliBehaviour,
     ) -> Result<()> {
         if let AuthState::Unstarted = self.state {
             self.state = AuthState::MethodQuery;
             self.username = b.username()?;
 
-            let p: Packet = packets::ServiceRequest {
+            s.send(packets::ServiceRequest {
                 name: SSH_SERVICE_USERAUTH,
-            }.into();
-            resp.push(p.into()).trap()?;
+            })?;
 
-            let p: Packet = packets::UserauthRequest {
+            s.send(packets::UserauthRequest {
                 username: self.username.as_str().into(),
                 service: SSH_SERVICE_CONNECTION,
                 method: packets::AuthMethod::None,
-            }.into();
-            resp.push(p.into()).trap()?;
+            })?;
         }
         Ok(())
     }
@@ -182,14 +180,14 @@ impl CliAuth {
     pub async fn auth60<'b>(
         &'b mut self,
         auth60: &packets::Userauth60<'_>,
-        resp: &mut RespPackets<'b>,
         sess_id: &SessId,
         parse_ctx: &mut ParseContext,
+        s: &TrafSend<'_>,
     ) -> Result<()> {
         parse_ctx.cli_auth_type = None;
 
         match auth60 {
-            Userauth60::PkOk(pkok) => self.auth_pkok(pkok, resp, sess_id, parse_ctx),
+            Userauth60::PkOk(pkok) => self.auth_pkok(pkok, sess_id, parse_ctx, s),
             _ => todo!(),
         }
     }
@@ -197,9 +195,9 @@ impl CliAuth {
     fn auth_pkok<'b>(
         &'b mut self,
         pkok: &UserauthPkOk<'_>,
-        resp: &mut RespPackets<'b>,
         sess_id: &SessId,
         parse_ctx: &mut ParseContext,
+        s: &TrafSend<'_>,
     ) -> Result<()> {
         // We are only sending keys one at a time so they shouldn't
         // get out of sync. In future we could change it to send
@@ -237,7 +235,7 @@ impl CliAuth {
                         let rsig = &*rsig;
                         *psig = Some(Blob(rsig.into()))
                     }
-                    resp.push(p.into()).trap()?;
+                    s.send(p)?;
                     return Ok(());
                 }
             }
@@ -251,9 +249,9 @@ impl CliAuth {
     pub async fn failure<'b>(
         &'b mut self,
         failure: &packets::UserauthFailure<'_>,
-        b: &mut dyn CliBehaviour,
-        resp: &mut RespPackets<'b>,
         parse_ctx: &mut ParseContext,
+        s: &TrafSend<'_>,
+        b: &mut dyn CliBehaviour,
     ) -> Result<()> {
         parse_ctx.cli_auth_type = None;
         // TODO: look at existing self.state, handle the failure.
@@ -287,7 +285,7 @@ impl CliAuth {
 
         if let AuthState::Request { last_req, .. } = &self.state {
             let p = last_req.req_packet(&self.username, parse_ctx)?;
-            resp.push(p.into()).trap()?;
+            s.send(p)?;
         }
         Ok(())
     }
diff --git a/sshproto/src/client.rs b/sshproto/src/client.rs
index e2d61ff3de159f006268d7ba74652306c1ed1c7b..eef52c109acebf63008e115d9a93704464c4fa38 100644
--- a/sshproto/src/client.rs
+++ b/sshproto/src/client.rs
@@ -8,9 +8,9 @@ use snafu::prelude::*;
 
 use crate::{*, packets::ChannelOpen};
 use packets::{Packet, PubKey, ParseContext};
+use traffic::TrafSend;
 use sshnames::*;
 use cliauth::CliAuth;
-use conn::RespPackets;
 use sign::SignKey;
 use behaviour::CliBehaviour;
 use heapless::String;
@@ -28,13 +28,13 @@ impl Client {
 
     // pub fn check_hostkey(hostkey: )
 
-    pub(crate) fn auth_success(&mut self, resp: &mut RespPackets<'_>,
+    pub(crate) fn auth_success(&mut self,
         parse_ctx: &mut ParseContext,
+        s: &TrafSend,
         b: &mut dyn CliBehaviour) -> Result<()> {
 
         parse_ctx.cli_auth_type = None;
-        let p: Packet = packets::ServiceRequest { name: SSH_SERVICE_CONNECTION }.into();
-        resp.push(p.into()).trap()?;
+        s.send(packets::ServiceRequest { name: SSH_SERVICE_CONNECTION })?;
         self.auth.success(b)
     }
 
diff --git a/sshproto/src/conn.rs b/sshproto/src/conn.rs
index 4c5b830b20136754e02d91b4f013a8b5d8bd4f49..6c73b0fdbdc8d8083a9847223b885cd5957068f4 100644
--- a/sshproto/src/conn.rs
+++ b/sshproto/src/conn.rs
@@ -17,20 +17,11 @@ use client::Client;
 use encrypt::KeyState;
 use packets::{Packet,ParseContext};
 use server::Server;
-use traffic::{Traffic,PacketMaker};
+use traffic::{Traffic, TrafSend};
 use channel::{Channels, ChanEvent, ChanEventMaker};
 use config::MAX_CHANNELS;
 use kex::SessId;
 
-// TODO a max value needs to be analysed
-pub(crate) const MAX_RESPONSES: usize = 4;
-
-pub type RespPackets<'a> = heapless::Vec<PacketMaker<'a>, MAX_RESPONSES>;
-
-pub(crate) enum Handled<'a> {
-    Response(RespPackets<'a>),
-}
-
 /// The core state of a SSH instance.
 pub struct Conn<'a> {
     state: ConnState,
@@ -133,13 +124,12 @@ impl<'a> Conn<'a> {
         b: &mut Behaviour<'_>,
     ) -> Result<(), Error> {
         debug!("progress conn state {:?}", self.state);
-        let mut resp = RespPackets::new();
+        let s = TrafSend::new(traffic, keys);
         match self.state {
             ConnState::SendIdent => {
                 traffic.send_version(ident::OUR_VERSION)?;
-                let p = self.kex.make_kexinit(&self.algo_conf);
+                let p = self.kex.send_kexinit(&self.algo_conf, &s)?;
                 // TODO: first_follows would have a second packet here
-                resp.push(p.into()).trap()?;
                 self.state = ConnState::ReceiveIdent
             }
             ConnState::ReceiveIdent => {
@@ -152,7 +142,7 @@ impl<'a> Conn<'a> {
                 // and backpressure. can_output() should have a size check?
                 if traffic.can_output() {
                     if let ClientServer::Client(cli) = &mut self.cliserv {
-                        cli.auth.start(&mut resp, b.client()?).await?;
+                        cli.auth.start(&s, b.client()?).await?;
                     }
                 }
                 // send userauth request
@@ -162,9 +152,6 @@ impl<'a> Conn<'a> {
                 // TODO
             }
         }
-        for r in resp {
-            r.send_packet(traffic, keys)?;
-        }
 
         // TODO: if keys.seq > MAX_REKEY then we must rekey for security.
 
@@ -185,30 +172,27 @@ impl<'a> Conn<'a> {
     /// after `handle_payload()` runs.
     pub(crate) async fn handle_payload<'p>(
         &mut self, payload: &'p [u8], seq: u32,
-        keys: &mut KeyState, b: &mut Behaviour<'_>,
-    ) -> Result<Dispatched<'_>, Error> {
+        s: &TrafSend<'_>,
+        b: &mut Behaviour<'_>,
+    ) -> Result<Dispatched, Error> {
         let r = sshwire::packet_from_bytes(payload, &self.parse_ctx);
         match r {
-            Ok(p) => self.dispatch_packet(p, keys, b).await,
+            Ok(p) => self.dispatch_packet(p, s, b).await,
             Err(Error::UnknownPacket { number }) => {
                 trace!("Unimplemented packet type {number}");
-                let p: Packet = packets::Unimplemented { seq }.into();
-                let mut resp = RespPackets::new();
-                // unwrap is OK, single packet has space
-                resp.push(p.into()).unwrap();
-                Ok(Dispatched { resp, event: None })
+                s.send(packets::Unimplemented { seq })?;
+                Ok(Dispatched { event: None })
             }
             Err(e) => return Err(e),
         }
     }
 
     async fn dispatch_packet<'p>(
-        &mut self, packet: Packet<'p>, keys: &mut KeyState, b: &mut Behaviour<'_>,
-    ) -> Result<Dispatched<'_>, Error> {
+        &mut self, packet: Packet<'p>, s: &TrafSend<'_>, b: &mut Behaviour<'_>,
+    ) -> Result<Dispatched, Error> {
         // TODO: perhaps could consolidate packet allowed checks into a separate function
         // to run first?
         trace!("Incoming {packet:#?}");
-        let mut resp = RespPackets::new();
         let mut event = None;
         match packet {
             Packet::KexInit(_) => {
@@ -220,14 +204,12 @@ impl<'a> Conn<'a> {
                     output: None,
                 };
                 let r = self.kex.handle_kexinit(
+                    &packet,
                     self.cliserv.is_client(),
                     &self.algo_conf,
                     &self.remote_version,
-                    &packet,
+                    s,
                 )?;
-                if let Some(r) = r {
-                    resp.push(r.into()).trap()?;
-                }
             }
             Packet::KexDHInit(p) => {
                 match self.state {
@@ -241,9 +223,7 @@ impl<'a> Conn<'a> {
                         } else {
                             let kex =
                                 core::mem::replace(&mut self.kex, kex::Kex::new()?);
-                            *output = Some(kex.handle_kexdhinit(&p, &self.sess_id)?);
-                            let reply = output.as_mut().trap()?.make_kexdhreply(b.server()?).await?;
-                            resp.push(reply.into()).trap()?;
+                            *output = Some(kex.handle_kexdhinit(&p, &self.sess_id, s, b.server()?)?);
                         }
                     }
                     _ => return Err(Error::PacketWrong),
@@ -258,8 +238,7 @@ impl<'a> Conn<'a> {
                             } else {
                                 let kex =
                                     core::mem::replace(&mut self.kex, kex::Kex::new()?);
-                                *output = Some(kex.handle_kexdhreply(&p, &self.sess_id, b.client()?).await?);
-                                resp.push(Packet::NewKeys(packets::NewKeys {}).into()).trap()?;
+                                *output = Some(kex.handle_kexdhreply(&p, &self.sess_id, s, b.client()?).await?);
                             }
                         } else {
                             // TODO: client/server validity checks should move somewhere more general
@@ -274,7 +253,7 @@ impl<'a> Conn<'a> {
                     ConnState::InKex { done_auth, ref mut output } => {
                         // NewKeys shouldn't be received before kexdhinit/kexdhreply
                         let output = output.take().ok_or(Error::PacketWrong)?;
-                        keys.rekey(output.keys);
+                        s.rekey(output.keys);
                         self.sess_id.get_or_insert(output.h);
                         self.state = if done_auth {
                             ConnState::Authed
@@ -313,7 +292,7 @@ impl<'a> Conn<'a> {
             Packet::UserauthFailure(p) => {
                 // TODO: client only
                 if let ClientServer::Client(cli) = &mut self.cliserv {
-                    cli.auth.failure(&p, b.client()?, &mut resp, &mut self.parse_ctx).await?;
+                    cli.auth.failure(&p, &mut self.parse_ctx, s, b.client()?).await?;
                 } else {
                     debug!("Received UserauthFailure as a server");
                     return Err(Error::SSHProtoError)
@@ -324,7 +303,7 @@ impl<'a> Conn<'a> {
                 if let ClientServer::Client(cli) = &mut self.cliserv {
                     if matches!(self.state, ConnState::PreAuth) {
                         self.state = ConnState::Authed;
-                        cli.auth_success(&mut resp, &mut self.parse_ctx, b.client()?)?;
+                        cli.auth_success(&mut self.parse_ctx, s, b.client()?)?;
                         event = Some(EventMaker::CliAuthed);
                     } else {
                         debug!("Received UserauthSuccess unrequested")
@@ -346,7 +325,7 @@ impl<'a> Conn<'a> {
             Packet::Userauth60(p) => {
                 // TODO: client only
                 if let ClientServer::Client(cli) = &mut self.cliserv {
-                    cli.auth.auth60(&p, &mut resp, self.sess_id.as_ref().trap()?, &mut self.parse_ctx).await?;
+                    cli.auth.auth60(&p, self.sess_id.as_ref().trap()?, &mut self.parse_ctx, s).await?;
                 } else {
                     debug!("Received userauth60 as a server");
                     return Err(Error::SSHProtoError)
@@ -365,11 +344,11 @@ impl<'a> Conn<'a> {
             | Packet::ChannelFailure(_)
             // TODO: maybe needs a conn or cliserv argument.
             => {
-                let chev = self.channels.dispatch(packet, &mut resp, b).await?;
+                let chev = self.channels.dispatch(packet, s, b).await?;
                 event = chev.map(|c| EventMaker::Channel(c))
            }
         };
-        Ok(Dispatched { resp, event })
+        Ok(Dispatched { event })
     }
 
     /// creates an `Event` that borrows data from the payload. Some `Event` variants don't
@@ -394,13 +373,8 @@ impl<'a> Conn<'a> {
 
 }
 
-// pub(crate) struct Dispatched<'r, 'e> {
-//     pub resp: RespPackets<'r>,
-//     pub event: Option<Event<'e>>,
-// }
-
-pub(crate) struct Dispatched<'r> {
-    pub resp: RespPackets<'r>,
+// TODO: delete this
+pub(crate) struct Dispatched {
     pub event: Option<EventMaker>,
 }
 
diff --git a/sshproto/src/kex.rs b/sshproto/src/kex.rs
index 88543e83cc90ed8c8cf544fa4c25936d85d8ff94..431f68d63b00614a6b92b2516348cc9b6a80f644 100644
--- a/sshproto/src/kex.rs
+++ b/sshproto/src/kex.rs
@@ -14,6 +14,7 @@ use digest::Digest;
 use crate::*;
 use encrypt::{Cipher, Integ, Keys};
 use ident::RemoteVersion;
+use traffic::TrafSend;
 use namelist::LocalNames;
 use packets::{Packet, PubKey, Signature};
 use sign::SigType;
@@ -198,11 +199,11 @@ impl Kex {
         Ok(Kex { our_cookie, algos: None, kex_hash: None })
     }
 
-    /// Returns `Option<Packet>` with an optional kexdhinit message to send
     pub fn handle_kexinit(
-        &mut self, is_client: bool, algo_conf: &AlgoConfig,
-        remote_version: &RemoteVersion, p: &packets::Packet,
-    ) -> Result<Option<Packet>> {
+        &mut self, p: &packets::Packet, is_client: bool, algo_conf: &AlgoConfig,
+        remote_version: &RemoteVersion,
+        s: &TrafSend,
+    ) -> Result<()> {
         let remote_kexinit =
             if let Packet::KexInit(k) = p { k } else { return Err(Error::bug()) };
         let algos = Self::algo_negotiation(is_client, remote_kexinit, algo_conf)?;
@@ -212,10 +213,12 @@ impl Kex {
         self.algos = Some(algos);
 
         if is_client {
-            Ok(Some(self.algos.as_ref().trap()?.kex.make_kexdhinit()?))
-        } else {
-            Ok(None)
+            // unwrap safe: was just set
+            let p = self.algos.as_ref().unwrap().kex.make_kexdhinit()?;
+            s.send(p)?;
         }
+
+        Ok(())
     }
 
     pub fn maybe_discard_packet(&mut self) -> bool {
@@ -226,8 +229,8 @@ impl Kex {
         }
     }
 
-    pub fn make_kexinit<'a>(&self, conf: &'a AlgoConfig) -> packets::Packet<'a> {
-        packets::KexInit {
+    pub fn send_kexinit<'a>(&self, conf: &'a AlgoConfig, s: &TrafSend) -> Result<()> {
+        s.send(packets::KexInit {
             cookie: self.our_cookie,
             kex: (&conf.kexs).into(),
             hostkey: (&conf.hostsig).into(),
@@ -241,7 +244,7 @@ impl Kex {
             lang_s2c: (&EMPTY_LOCALNAMES).into(),
             first_follows: false,
             reserved: 0,
-        }.into()
+        })
     }
 
     fn make_kexdhinit(&self) -> Result<Packet> {
@@ -252,27 +255,32 @@ impl Kex {
         algos.kex.make_kexdhinit()
     }
 
-    // returns packet to send, and kex output
-    // consumes self.
+    // returns kex output, consumes self.
     pub fn handle_kexdhinit<'a>(
         self, p: &packets::KexDHInit, sess_id: &Option<SessId>,
+        s: &TrafSend, b: &mut dyn ServBehaviour,
     ) -> Result<KexOutput> {
         if self.algos.as_ref().trap()?.is_client {
             return Err(Error::bug());
         }
-        SharedSecret::handle_kexdhinit(self, p, sess_id)
+        let kex_out = SharedSecret::handle_kexdhinit(self, p, sess_id, s, b)?;
+        s.send(packets::NewKeys {})?;
+        Ok(kex_out)
     }
 
     // returns packet to send, and H exchange hash.
     // consumes self.
     pub async fn handle_kexdhreply<'f>(
         self, p: &packets::KexDHReply<'f>, sess_id: &Option<SessId>,
+        s: &TrafSend<'_>,
         b: &mut dyn CliBehaviour,
     ) -> Result<KexOutput> {
         if !self.algos.as_ref().trap()?.is_client {
             return Err(Error::bug());
         }
-        SharedSecret::handle_kexdhreply(self, p, sess_id, b).await
+        let kex_out = SharedSecret::handle_kexdhreply(self, p, sess_id, b).await?;
+        s.send(packets::NewKeys {})?;
+        Ok(kex_out)
     }
 
     /// Perform SSH algorithm negotiation
@@ -434,6 +442,7 @@ impl SharedSecret {
     // server only. consumes kex.
     fn handle_kexdhinit<'a>(
         mut kex: Kex, p: &packets::KexDHInit, sess_id: &Option<SessId>,
+        s: &TrafSend, b: &mut dyn ServBehaviour,
     ) -> Result<KexOutput> {
         // let mut algos = kex.algos.take().trap()?;
         let mut algos = kex.algos.trap()?;
@@ -441,19 +450,32 @@ impl SharedSecret {
         // TODO
         let fake_hostkey = PubKey::Ed25519(packets::Ed25519PubKey{ key: BinString(&[]) });
         kex_hash.prefinish(&fake_hostkey, p.q_c.0, algos.kex.pubkey())?;
-        let kex_out = match algos.kex {
+        let (kex_pub, kex_out) = match algos.kex {
             SharedSecret::KexCurve25519(ref k) => {
                 let pubkey: salty::agreement::PublicKey = k.ours.as_ref().trap()?.into();
-                let mut kex_out = KexCurve25519::secret(&mut algos, p.q_c.0, kex_hash, sess_id)?;
-                kex_out.kex_pub = Some(pubkey.to_bytes());
-                kex_out
+                let kex_out = KexCurve25519::secret(&mut algos, p.q_c.0, kex_hash, sess_id)?;
+                (&pubkey.to_bytes(), kex_out)
             }
         };
 
+        kex.send_kexdhreply(kex_pub, s, b)?;
         Ok(kex_out)
     }
 
-    fn pubkey<'a>(&'a self) -> &'a [u8] {
+    // server only
+    pub fn send_kexdhreply(&self, kex_pub: &[u8], s: &TrafSend, b: &mut dyn ServBehaviour) -> Result<()> {
+        let q_s = BinString(kex_pub);
+
+        // hostkeys list must contain the signature type
+        let key = b.hostkeys()?.iter().find(|k| k.can_sign(&self.algos.hostsig)).trap()?;
+        let k_s = Blob(key.pubkey());
+        self.sig = Some(key.sign(&self.h.as_slice(), None)?);
+        let sig: Signature = self.sig.as_ref().unwrap().into();
+        let sig = Blob(sig);
+        Ok(packets::KexDHReply { k_s, q_s, sig }.into())
+    }
+
+    fn pubkey(&self) -> &[u8] {
         match self {
             SharedSecret::KexCurve25519(k) => k.pubkey(),
         }
@@ -491,21 +513,9 @@ impl<'a> KexOutput {
         let sess_id = sess_id.as_ref().unwrap_or(&h);
         let keys = Keys::new_from(k, &h, &sess_id, algos)?;
 
-        Ok(KexOutput { h, keys, kex_pub: None, sig_type: algos.hostsig, sig: None })
+        Ok(KexOutput { h, keys })
     }
 
-    // server only
-    pub async fn make_kexdhreply<'b>(&'a mut self, b: &'a mut dyn ServBehaviour) -> Result<Packet<'a>> {
-        let q_s = BinString(self.kex_pub.as_ref().trap()?);
-
-        // hostkeys list must contain the signature type
-        let key = b.hostkeys()?.iter().find(|k| k.can_sign(&self.sig_type)).trap()?;
-        let k_s = Blob(key.pubkey());
-        self.sig = Some(key.sign(&self.h.as_slice(), None)?);
-        let sig: Signature = self.sig.as_ref().unwrap().into();
-        let sig = Blob(sig);
-        Ok(packets::KexDHReply { k_s, q_s, sig }.into())
-    }
 }
 
 pub(crate) struct KexCurve25519 {
diff --git a/sshproto/src/lib.rs b/sshproto/src/lib.rs
index 9a98b0cb2beff6bbf725a03a0df7ac73c777c626..9dac0a9a675c42f8a2e3b28a6992dd63be1d6b7b 100644
--- a/sshproto/src/lib.rs
+++ b/sshproto/src/lib.rs
@@ -47,7 +47,6 @@ pub mod config;
 pub use behaviour::{Behaviour, ServBehaviour, CliBehaviour, BhError, BhResult, ResponseString};
 
 pub use runner::Runner;
-pub use conn::RespPackets;
 pub use sign::SignKey;
 pub use packets::PubKey;
 pub use error::{Error,Result};
diff --git a/sshproto/src/runner.rs b/sshproto/src/runner.rs
index 19ade4a165282d83ad6fcd804a7c78ff5d852f35..dc313634e2d580143e25656470c167ea01bf1b9a 100644
--- a/sshproto/src/runner.rs
+++ b/sshproto/src/runner.rs
@@ -10,7 +10,7 @@ use pretty_hex::PrettyHex;
 
 use crate::{*, channel::ChanEvent};
 use encrypt::KeyState;
-use traffic::Traffic;
+use traffic::{Traffic, TrafSend};
 
 use conn::{Conn, Dispatched, EventMaker, Event};
 use channel::ChanEventMaker;
@@ -95,16 +95,15 @@ impl<'a> Runner<'a> {
             // by the send_packet().
             // After that progress() can perform more send_packet() itself.
 
-            let d = self.conn.handle_payload(payload, seq, &mut self.keys, behaviour).await?;
+            // TODO matt aug: trafsend should be constructed by traffic.split_send() or something.
+            let s = TrafSend::new(&mut self.traffic, &mut self.keys);
+            let d = self.conn.handle_payload(payload, seq, &s, behaviour).await?;
             self.traffic.handled_payload()?;
 
-            if !d.resp.is_empty() || d.event.is_none() {
+            if d.event.is_none() {
                 // switch to using the buffer for output.
                 self.traffic.done_payload()?;
             }
-            for r in d.resp {
-                r.send_packet(&mut self.traffic, &mut self.keys)?;
-            }
 
             d.event
         } else {
diff --git a/sshproto/src/traffic.rs b/sshproto/src/traffic.rs
index 31269034a7460be5f6a9ea387e719eacb8a07de9..a8d123f21d59c54019629f28f3cee7e6b69fe947 100644
--- a/sshproto/src/traffic.rs
+++ b/sshproto/src/traffic.rs
@@ -78,33 +78,6 @@ enum RxState {
     },
 }
 
-#[derive(Debug)]
-pub enum PacketMaker<'a> {
-    Packet(Packet<'a>),
-    ChanReq(channel::Req),
-}
-
-impl<'a> From<Packet<'a>> for PacketMaker<'a> {
-    fn from(p: Packet<'a>) -> Self {
-        PacketMaker::Packet(p)
-    }
-}
-
-impl From<channel::Req> for PacketMaker<'_> {
-    fn from(r: channel::Req) -> Self {
-        PacketMaker::ChanReq(r)
-    }
-}
-
-impl<'a> PacketMaker<'a> {
-    pub(crate) fn send_packet(self, traffic: &mut Traffic, keys: &mut KeyState) -> Result<()> {
-        match self {
-            Self::Packet(p) => traffic.send_packet(p, keys),
-            Self::ChanReq(r) => traffic.send_packet(r.packet()?, keys),
-        }
-    }
-}
-
 impl<'a> Traffic<'a> {
     pub fn new(rx_buf: &'a mut [u8], tx_buf: &'a mut [u8]) -> Self {
         Traffic { tx_buf, rx_buf,
@@ -415,3 +388,25 @@ impl<'a> Traffic<'a> {
     }
 
 }
+
+pub(crate) struct TrafSend<'a> {
+    traffic: &'a mut Traffic<'a>,
+    keys: &'a mut KeyState,
+}
+
+impl<'a> TrafSend<'a> {
+    pub fn new(traffic: &mut Traffic, keys: &mut KeyState) -> Self {
+        Self {
+            traffic,
+            keys,
+        }
+    }
+
+    pub fn send<'p, P: Into<packets::Packet<'p>>>(&self, p: P) -> Result<()> {
+        self.traffic.send_packet(p.into(), self.keys)
+    }
+
+    pub fn rekey(&self, keys: encrypt::Keys) {
+        self.keys.rekey(keys)
+    }
+}