diff --git a/async/examples/con1.rs b/async/examples/con1.rs
index c973e24d62dda393378eaeb0371dfd60c260b45c..b1e3b88e74f38733fb11af2e7167a2fb39bc91d7 100644
--- a/async/examples/con1.rs
+++ b/async/examples/con1.rs
@@ -145,12 +145,13 @@ async fn run(args: &Args) -> Result<()> {
     let tx = vec![0; 3000];
     let tx = Box::leak(Box::new(tx)).as_mut_slice();
 
+    // cli is a Behaviour
     let mut cli = door_async::CmdlineClient::new(args.username.as_ref().unwrap());
     for i in &args.identityfile {
         cli.add_authkey(read_key(&i).with_context(|| format!("loading key {i}"))?);
     }
 
-    let mut door = SSHClient::new(work, tx, Box::new(cli))?;
+    let mut door = SSHClient::new(work, tx)?;
     let mut s = door.socket();
 
     moro::async_scope!(|scope| {
@@ -158,7 +159,7 @@ async fn run(args: &Args) -> Result<()> {
 
         scope.spawn(async {
             loop {
-                let ev = door.progress(|ev| {
+                let ev = door.progress(&mut cli, |ev| {
                     trace!("progress event {ev:?}");
                     let e = match ev {
                         Event::CliAuthed => Some(Event::CliAuthed),
diff --git a/async/src/async_door.rs b/async/src/async_door.rs
index 3db916a9f8127e67b33655914a017a57a681feaf..df4419f381571ff7308883d12f327263c2220206 100644
--- a/async/src/async_door.rs
+++ b/async/src/async_door.rs
@@ -19,7 +19,7 @@ use core::ops::DerefMut;
 use std::sync::Arc;
 
 use door_sshproto as door;
-use door::{Runner, Result, Event, ChanEvent};
+use door::{Runner, Result, Event, ChanEvent, Behaviour};
 
 use pretty_hex::PrettyHex;
 
@@ -59,7 +59,9 @@ impl<'a> AsyncDoor<'a> {
         AsyncDoorSocket::new(self)
     }
 
-    pub async fn progress<F, R>(&mut self, f: F)
+    pub async fn progress<F, R>(&mut self,
+        b: &mut Behaviour<'_>,
+        f: F)
         -> Result<Option<R>>
         where F: FnOnce(door::Event) -> Result<Option<R>> {
         trace!("progress");
@@ -67,7 +69,7 @@ impl<'a> AsyncDoor<'a> {
         let res = {
             let mut inner = self.inner.lock().await;
             let inner = inner.deref_mut();
-            let ev = inner.runner.progress().await?;
+            let ev = inner.runner.progress(b).await?;
             let r = if let Some(ev) = ev {
                 let r = match ev {
                     Event::Channel(ChanEvent::Eof { num }) => {
diff --git a/async/src/client.rs b/async/src/client.rs
index cf63026d73cb8aea66e63d813fd3012a558ca5b6..63030291db19fa436f5d0c51d0e00bf7ea179ba0 100644
--- a/async/src/client.rs
+++ b/async/src/client.rs
@@ -31,10 +31,8 @@ pub struct SSHClient<'a> {
 
 impl<'a> SSHClient<'a> {
     pub fn new(inbuf: &'a mut [u8],
-        outbuf: &'a mut [u8],
-        behaviour: Box<dyn AsyncCliBehaviour+Send>) -> Result<Self> {
-        let b = Behaviour::new_async_client(behaviour);
-        let runner = Runner::new_client(inbuf, outbuf, b)?;
+        outbuf: &'a mut [u8]) -> Result<Self> {
+        let runner = Runner::new_client(inbuf, outbuf)?;
         let door = AsyncDoor::new(runner);
         Ok(Self {
             door
@@ -45,10 +43,17 @@ impl<'a> SSHClient<'a> {
         self.door.socket()
     }
 
-    pub async fn progress<F, R>(&mut self, f: F)
+    /// Takes a closure to run on the "output" of the progress call.
+    /// (This output can't be returned directly since it refers
+    /// to contents of `Self` and would hit lifetime issues).
+    pub async fn progress<F, R>(&mut self,
+        behaviour: &mut (dyn AsyncCliBehaviour+Send),
+        f: F)
         -> Result<Option<R>>
         where F: FnOnce(door::Event) -> Result<Option<R>> {
-        self.door.progress(f).await
+
+        let mut b = Behaviour::new_async_client(behaviour);
+        self.door.progress(&mut b, f).await
     }
 
     // TODO: return a Channel object that gives events like WinChange or exit status
diff --git a/sshproto/src/async_behaviour.rs b/sshproto/src/async_behaviour.rs
index a180584f176495d052193f0eb94e60bc25638378..2dba4729481949e516c93811e7b0b3171889594a 100644
--- a/sshproto/src/async_behaviour.rs
+++ b/sshproto/src/async_behaviour.rs
@@ -9,24 +9,23 @@ use {
 };
 
 use async_trait::async_trait;
-use std::boxed::Box;
 
 use crate::{*, conn::RespPackets};
 use behaviour::*;
 
-pub(crate) enum AsyncCliServ {
-    Client(Box<dyn AsyncCliBehaviour + Send>),
-    Server(Box<dyn AsyncServBehaviour + Send>),
+pub(crate) enum AsyncCliServ<'a> {
+    Client(&'a mut (dyn AsyncCliBehaviour + Send)),
+    Server(&'a mut (dyn AsyncServBehaviour + Send)),
 }
 
-impl AsyncCliServ {
+impl<'a> AsyncCliServ<'a> {
     pub fn client(&mut self) -> Result<CliBehaviour> {
         let c = match self {
             Self::Client(c) => c,
             _ => Error::bug_msg("Not client")?,
         };
         let c = CliBehaviour {
-            inner: c.as_mut(),
+            inner: *c,
         };
         Ok(c)
     }
@@ -37,8 +36,7 @@ impl AsyncCliServ {
             _ => Error::bug_msg("Not server")?,
         };
         let c = ServBehaviour {
-            inner: c.as_mut(),
-            phantom: core::marker::PhantomData::default(),
+            inner: *c,
         };
         Ok(c)
     }
diff --git a/sshproto/src/behaviour.rs b/sshproto/src/behaviour.rs
index 914148e16aa08491dfc3e2d02d054e78b2223e47..8fd0e68bc0bfa0f6ad86d465c536a435daf5d1d8 100644
--- a/sshproto/src/behaviour.rs
+++ b/sshproto/src/behaviour.rs
@@ -47,7 +47,7 @@ pub type ReplyChannel = bhtokio::ReplyChannel;
 
 pub struct Behaviour<'a> {
     #[cfg(feature = "std")]
-    inner: crate::async_behaviour::AsyncCliServ,
+    inner: crate::async_behaviour::AsyncCliServ<'a>,
     #[cfg(not(feature = "std"))]
     inner: crate::block_behaviour::BlockCliServ<'a>,
 
@@ -55,15 +55,15 @@ pub struct Behaviour<'a> {
 }
 
 #[cfg(feature = "std")]
-impl Behaviour<'_> {
-    pub fn new_async_client(b: Box<dyn async_behaviour::AsyncCliBehaviour + Send>) -> Self {
+impl<'a> Behaviour<'a> {
+    pub fn new_async_client(b: &'a mut (dyn AsyncCliBehaviour + Send)) -> Self {
         Self {
             inner: async_behaviour::AsyncCliServ::Client(b),
             phantom: PhantomData::default(),
         }
     }
 
-    pub fn new_async_server(b: Box<dyn async_behaviour::AsyncServBehaviour + Send>) -> Self {
+    pub fn new_async_server(b: &'a mut (dyn AsyncServBehaviour + Send)) -> Self {
         Self {
             inner: async_behaviour::AsyncCliServ::Server(b),
             phantom: PhantomData::default(),
@@ -86,14 +86,12 @@ impl<'a> Behaviour<'a>
     pub fn new_blocking_client(b: &'a mut dyn BlockCliBehaviour) -> Self {
         Self {
             inner: block_behaviour::BlockCliServ::Client(b),
-            phantom: PhantomData::default(),
         }
     }
 
     pub fn new_blocking_server(b: &'a mut dyn BlockServBehaviour) -> Self {
         Self {
             inner: block_behaviour::BlockCliServ::Server(b),
-            phantom: PhantomData::default(),
         }
     }
 
@@ -184,7 +182,6 @@ pub struct ServBehaviour<'a> {
     pub inner: &'a mut dyn async_behaviour::AsyncServBehaviour,
     #[cfg(not(feature = "std"))]
     pub inner: &'a mut dyn block_behaviour::BlockServBehaviour,
-    pub phantom: core::marker::PhantomData<&'a ()>,
 }
 
 #[cfg(feature = "std")]
diff --git a/sshproto/src/block_behaviour.rs b/sshproto/src/block_behaviour.rs
index 945db7fe2fbf52ac42096264772b59d813ecd165..86240f1f579aa3579c39b7159550c048a1d3a58d 100644
--- a/sshproto/src/block_behaviour.rs
+++ b/sshproto/src/block_behaviour.rs
@@ -35,7 +35,6 @@ impl BlockCliServ<'_>
         };
         let c = ServBehaviour {
             inner: *c,
-            phantom: core::marker::PhantomData::default(),
         };
         Ok(c)
     }
diff --git a/sshproto/src/runner.rs b/sshproto/src/runner.rs
index 4c99e4db97e55c89b2eb6993cc41b5d85ece53ae..ddae3a0e1a9b9477e16ac437659cf4841ce12713 100644
--- a/sshproto/src/runner.rs
+++ b/sshproto/src/runner.rs
@@ -18,8 +18,6 @@ use channel::ChanEventMaker;
 pub struct Runner<'a> {
     conn: Conn<'a>,
 
-    behaviour: Behaviour<'a>,
-
     /// Binary packet handling to and from the network buffer
     traffic: Traffic<'a>,
 
@@ -35,7 +33,6 @@ impl<'a> Runner<'a> {
     pub fn new_client(
         inbuf: &'a mut [u8],
         outbuf: &'a mut [u8],
-        behaviour: Behaviour<'a>,
     ) -> Result<Runner<'a>, Error> {
         let conn = Conn::new_client()?;
         let runner = Runner {
@@ -44,7 +41,6 @@ impl<'a> Runner<'a> {
             keys: KeyState::new_cleartext(),
             output_waker: None,
             input_waker: None,
-            behaviour,
         };
 
         Ok(runner)
@@ -74,7 +70,7 @@ impl<'a> Runner<'a> {
     /// Optionally returns `Event` which provides channel or session
     /// event to the application.
     /// [`done_payload()`] must be called after any `Ok` result.
-    pub async fn progress<'f>(&'f mut self) -> Result<Option<Event<'f>>, Error> {
+    pub async fn progress<'f>(&'f mut self, behaviour: &mut Behaviour<'_>) -> Result<Option<Event<'f>>, Error> {
         let em = if let Some((payload, seq)) = self.traffic.payload() {
             // Lifetimes here are a bit subtle.
             // `payload` has self.traffic lifetime, used until `handle_payload`
@@ -83,7 +79,7 @@ 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, &mut self.behaviour).await?;
+            let d = self.conn.handle_payload(payload, seq, &mut self.keys, behaviour).await?;
             self.traffic.handled_payload()?;
 
             if !d.resp.is_empty() || d.event.is_none() {
@@ -123,7 +119,7 @@ impl<'a> Runner<'a> {
             }
         } else {
             trace!("no em, conn progress");
-            self.conn.progress(&mut self.traffic, &mut self.keys, &mut self.behaviour).await?;
+            self.conn.progress(&mut self.traffic, &mut self.keys, behaviour).await?;
             self.wake();
             None
         };