diff --git a/Cargo.toml b/Cargo.toml
index 61916f6..361ba7a 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -82,5 +82,3 @@ simplelog = { version = "0.12", features = ["test"] }
 
 
 [patch.crates-io]
-# needed for Default WakerRegistration, https://github.com/embassy-rs/embassy/commit/14a2d1524080593f7795fe14950a3f0ee6e2b409
-embassy-sync = { git = "https://github.com/embassy-rs/embassy", rev = "e1eac15c429f88b1176109d6ce42185e2774ac86" }
diff --git a/async/Cargo.toml b/async/Cargo.toml
index 39b2929..39020ea 100644
--- a/async/Cargo.toml
+++ b/async/Cargo.toml
@@ -16,7 +16,7 @@ argh = "0.1"
 
 ssh-key = { version = "0.5", default-features = false, features = [ "std"] }
 
-embassy-sync = { version = "0.1.0" }
+embassy-sync = { version = "0.2.0" }
 embassy-futures = { version = "0.1.0" }
 
 # async feature requires nightly for async fn in trait
diff --git a/async/src/known_hosts.rs b/async/src/known_hosts.rs
index eb7a166..2fe063a 100644
--- a/async/src/known_hosts.rs
+++ b/async/src/known_hosts.rs
@@ -100,7 +100,7 @@ pub fn check_known_hosts_file(
         let known_key = match OpenSSHKey::from_openssh(&lk) {
             Ok(k) => k,
             Err(e) => {
-                warn!("Unparsed key for {} on line {}:{}", host, p.display(), line);
+                warn!("Unparsed key for \"{}\" on line {}:{}", host, p.display(), line);
                 trace!("{e:?}");
                 continue;
             }
diff --git a/embassy/Cargo.toml b/embassy/Cargo.toml
index 586ce67..d90a643 100644
--- a/embassy/Cargo.toml
+++ b/embassy/Cargo.toml
@@ -4,7 +4,7 @@ version = "0.2.0-alpha"
 edition = "2021"
 
 [dependencies]
-embassy-sync = { version = "0.1.0" }
+embassy-sync = { version = "0.2.0" }
 embassy-futures = { version = "0.1.0" }
 embedded-io = { version = "0.4", features = ["async"] }
 atomic-polyfill = "1.0"
diff --git a/embassy/demos/common/Cargo.toml b/embassy/demos/common/Cargo.toml
new file mode 100644
index 0000000..7b68009
--- /dev/null
+++ b/embassy/demos/common/Cargo.toml
@@ -0,0 +1,29 @@
+[package]
+name = "sunset-demo-embassy-common"
+description = "Shared code for Sunset demos"
+version = "0.1.0"
+edition = "2021"
+
+[workspace]
+# blank
+
+[dependencies]
+sunset-embassy = { path = "../../" }
+sunset = { path = "../../.." }
+sunset-sshwire-derive = { version = "0.1", path = "../../../sshwire-derive" }
+
+embassy-sync = { version = "0.2.0" }
+embassy-net = { version = "0.1.0", features = ["tcp", "dhcpv4", "medium-ethernet", "nightly"] }
+embassy-net-driver = { version = "0.1.0" }
+embassy-futures = { version = "0.1.0" }
+
+heapless = "0.7.15"
+menu = "0.3"
+embedded-io = { version = "0.4", features = ["async"] }
+
+defmt = { version = "0.3", optional = true }
+log = { version = "0.4", optional = true }
+
+[features]
+defmt = ["dep:defmt", "embassy-net/defmt", "embedded-io/defmt"]
+log = ["dep:log", "embassy-net/log"]
diff --git a/embassy/demos/common/README.md b/embassy/demos/common/README.md
index 225de75..89ced50 100644
--- a/embassy/demos/common/README.md
+++ b/embassy/demos/common/README.md
@@ -1,4 +1,3 @@
 # Embassy demos common
 
-The [picow](../picow) and [std](../std) demos share this common code. 
-Currently not a full crate, just source modules included.
+The [picow](../picow) and [std](../std) demos share this common code.
diff --git a/embassy/demos/common/src/config.rs b/embassy/demos/common/src/config.rs
new file mode 100644
index 0000000..1cb6ec5
--- /dev/null
+++ b/embassy/demos/common/src/config.rs
@@ -0,0 +1,108 @@
+#[allow(unused_imports)]
+use {
+    sunset::error::{Error, Result, TrapBug},
+};
+
+#[allow(unused_imports)]
+#[cfg(not(feature = "defmt"))]
+use {
+    log::{debug, error, info, log, trace, warn},
+};
+
+#[allow(unused)]
+#[cfg(feature = "defmt")]
+use defmt::{debug, info, warn, panic, error, trace};
+
+use heapless::String;
+
+use sunset_sshwire_derive::*;
+use sunset::sshwire;
+use sunset::sshwire::{BinString, SSHEncode, SSHDecode, WireResult, SSHSource, SSHSink, WireError};
+
+use sunset::{SignKey, KeyType};
+
+// Be sure to bump picow flash_config::CURRENT_VERSION
+// if this struct changes (or encode/decode impls).
+#[derive(Debug)]
+pub struct SSHConfig {
+    pub hostkey: SignKey,
+    /// login password
+    pub pw_hash: Option<[u8; 32]>,
+    /// SSID
+    pub wifi_net: String<32>,
+    /// WPA2 passphrase. None is Open network.
+    pub wifi_pw: Option<String<63>>,
+}
+
+impl SSHConfig {
+    /// Creates a new config with default parameters.
+    ///
+    /// Will only fail on RNG failure.
+    pub fn new() -> Result<Self> {
+        let hostkey = SignKey::generate(KeyType::Ed25519, None)?;
+
+        let wifi_net = option_env!("WIFI_NETWORK").unwrap_or("guest").into();
+        let wifi_pw = option_env!("WIFI_PASSWORD").map(|p| p.into());
+        Ok(SSHConfig {
+            hostkey,
+            pw_hash: None,
+            wifi_net,
+            wifi_pw,
+        })
+    }
+}
+
+// a private encoding specific to demo config, not SSH defined.
+fn enc_signkey(k: &SignKey, s: &mut dyn SSHSink) -> WireResult<()> {
+    // need to add a variant field if we support more key types.
+    match k {
+        SignKey::Ed25519(seed) => seed.enc(s),
+        _ => Err(WireError::UnknownVariant),
+    }
+}
+
+fn dec_signkey<'de, S>(s: &mut S) -> WireResult<SignKey> where S: SSHSource<'de> {
+    Ok(SignKey::Ed25519(SSHDecode::dec(s)?))
+}
+
+impl SSHEncode for SSHConfig {
+    fn enc(&self, s: &mut dyn SSHSink) -> WireResult<()> {
+        enc_signkey(&self.hostkey, s)?;
+        self.pw_hash.is_some().enc(s)?;
+        self.pw_hash.enc(s)?;
+
+        self.wifi_net.as_str().enc(s)?;
+
+        self.wifi_pw.is_some().enc(s)?;
+        if let Some(ref p) = self.wifi_pw {
+            p.as_str().enc(s)?;
+        }
+        Ok(())
+    }
+}
+
+impl<'de> SSHDecode<'de> for SSHConfig {
+    fn dec<S>(s: &mut S) -> WireResult<Self> where S: SSHSource<'de> {
+        let hostkey = dec_signkey(s)?;
+
+        let have_pw_hash = bool::dec(s)?;
+        let pw_hash = have_pw_hash.then(|| SSHDecode::dec(s)).transpose()?;
+
+        let wifi_net = <&str>::dec(s)?.into();
+        let have_wifi_pw = bool::dec(s)?;
+
+        let wifi_pw = have_wifi_pw.then(|| {
+            let p: &str = SSHDecode::dec(s)?;
+            Ok(p.into())
+        })
+        .transpose()?;
+        Ok(Self {
+            hostkey,
+            pw_hash,
+            wifi_net,
+            wifi_pw,
+        })
+    }
+}
+
+
diff --git a/embassy/demos/common/demo_menu.rs b/embassy/demos/common/src/demo_menu.rs
similarity index 99%
rename from embassy/demos/common/demo_menu.rs
rename to embassy/demos/common/src/demo_menu.rs
index 80373f0..81c69c5 100644
--- a/embassy/demos/common/demo_menu.rs
+++ b/embassy/demos/common/src/demo_menu.rs
@@ -1,5 +1,5 @@
 use menu::*;
-pub use super::BufOutput;
+pub use crate::server::BufOutput;
 use core::fmt::Write;
 
 // from menu crate examples/simple.rs
diff --git a/embassy/demos/common/src/lib.rs b/embassy/demos/common/src/lib.rs
new file mode 100644
index 0000000..d30e7b1
--- /dev/null
+++ b/embassy/demos/common/src/lib.rs
@@ -0,0 +1,13 @@
+#![no_std]
+
+#![feature(type_alias_impl_trait)]
+#![feature(async_fn_in_trait)]
+// #![allow(incomplete_features)]
+
+mod config;
+mod server;
+
+pub mod demo_menu;
+
+pub use server::{Shell, listener};
+pub use config::SSHConfig;
diff --git a/embassy/demos/common/server.rs b/embassy/demos/common/src/server.rs
similarity index 92%
rename from embassy/demos/common/server.rs
rename to embassy/demos/common/src/server.rs
index 310bffb..5c3d64f 100644
--- a/embassy/demos/common/server.rs
+++ b/embassy/demos/common/src/server.rs
@@ -21,30 +21,26 @@ use embedded_io::asynch;
 use sunset::*;
 use sunset_embassy::SSHServer;
 
-// TODO move
-pub mod demo_menu;
-
+use crate::SSHConfig;
+
+// #[macro_export]
+// macro_rules! singleton {
+//     ($val:expr) => {{
+//         type T = impl Sized;
+//         static STATIC_CELL: StaticCell<T> = StaticCell::new();
+//         STATIC_CELL.init($val)
+//     }};
+// }
 #[macro_export]
 macro_rules! singleton {
     ($val:expr) => {{
         type T = impl Sized;
         static STATIC_CELL: StaticCell<T> = StaticCell::new();
-        STATIC_CELL.init($val)
+        let (x,) = STATIC_CELL.init(($val,));
+        x
     }};
 }
 
-pub struct SSHConfig {
-    keys: [SignKey; 1],
-}
-
-impl SSHConfig {
-    pub fn new() -> Result<Self> {
-        let keys = [SignKey::generate(KeyType::Ed25519, None)?];
-        Ok(Self {
-            keys
-        })
-    }
-}
 
 // common entry point
 pub async fn listener<D: Driver, S: Shell>(stack: &'static Stack<D>, config: &SSHConfig) -> ! {
@@ -106,6 +102,9 @@ async fn session<S: Shell>(socket: &mut TcpSocket<'_>, config: &SSHConfig) -> su
 struct DemoServer<'a, S: Shell> {
     config: &'a SSHConfig,
 
+    // references config
+    hostkeys: [&'a SignKey; 1],
+
     handle: Option<ChanHandle>,
     sess: Option<ChanNum>,
 
@@ -120,13 +119,14 @@ impl<'a, S: Shell> DemoServer<'a, S> {
             sess: None,
             config,
             shell,
+            hostkeys: [&config.hostkey],
         })
     }
 }
 
 impl<'a, S: Shell> ServBehaviour for DemoServer<'a, S> {
-    fn hostkeys(&mut self) -> BhResult<&[SignKey]> {
-        Ok(&self.config.keys)
+    fn hostkeys(&mut self) -> BhResult<&[&SignKey]> {
+        Ok(&self.hostkeys)
     }
 
     fn auth_unchallenged(&mut self, username: TextString) -> bool {
diff --git a/embassy/demos/picow/Cargo.lock b/embassy/demos/picow/Cargo.lock
index 6200bf3..222f779 100644
--- a/embassy/demos/picow/Cargo.lock
+++ b/embassy/demos/picow/Cargo.lock
@@ -101,6 +101,12 @@ version = "1.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
 
+[[package]]
+name = "az"
+version = "1.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7b7e4c2464d97fe331d41de9d5db0def0a96f4d823b8b32a2efd503578988973"
+
 [[package]]
 name = "bare-metal"
 version = "0.2.5"
@@ -152,6 +158,12 @@ dependencies = [
  "generic-array 0.14.6",
 ]
 
+[[package]]
+name = "bytemuck"
+version = "1.13.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "17febce684fd15d89027105661fec94afb475cb995fbc59d2865198446ba2eea"
+
 [[package]]
 name = "byteorder"
 version = "1.4.3"
@@ -303,7 +315,7 @@ dependencies = [
 [[package]]
 name = "cyw43"
 version = "0.1.0"
-source = "git+https://github.com/embassy-rs/cyw43/?rev=46efce6ea2d4280d5e8e1eaece53269a94fb4847#46efce6ea2d4280d5e8e1eaece53269a94fb4847"
+source = "git+https://github.com/embassy-rs/cyw43/?rev=c19de2984751ba6fa2972ee66cfa2a6310d5f0c1#c19de2984751ba6fa2972ee66cfa2a6310d5f0c1"
 dependencies = [
  "atomic-polyfill 0.1.11",
  "cortex-m",
@@ -311,8 +323,8 @@ dependencies = [
  "defmt",
  "embassy-futures",
  "embassy-net-driver-channel",
- "embassy-sync",
- "embassy-time",
+ "embassy-sync 0.1.0",
+ "embassy-time 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "embedded-hal 1.0.0-alpha.10",
  "futures",
  "num_enum",
@@ -321,7 +333,7 @@ dependencies = [
 [[package]]
 name = "cyw43-pio"
 version = "0.1.0"
-source = "git+https://github.com/embassy-rs/cyw43/?rev=46efce6ea2d4280d5e8e1eaece53269a94fb4847#46efce6ea2d4280d5e8e1eaece53269a94fb4847"
+source = "git+https://github.com/embassy-rs/cyw43/?rev=c19de2984751ba6fa2972ee66cfa2a6310d5f0c1#c19de2984751ba6fa2972ee66cfa2a6310d5f0c1"
 dependencies = [
  "cyw43",
  "defmt",
@@ -466,7 +478,7 @@ checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91"
 [[package]]
 name = "embassy-cortex-m"
 version = "0.1.0"
-source = "git+https://github.com/embassy-rs/embassy?rev=5a03b2e9e802626127038cff5634795f576f1c69#5a03b2e9e802626127038cff5634795f576f1c69"
+source = "git+https://github.com/embassy-rs/embassy?rev=3e730aa8b06401003202bf9e21a9c83ec6b21b0e#3e730aa8b06401003202bf9e21a9c83ec6b21b0e"
 dependencies = [
  "atomic-polyfill 1.0.1",
  "cfg-if",
@@ -475,15 +487,15 @@ dependencies = [
  "embassy-executor",
  "embassy-hal-common",
  "embassy-macros",
- "embassy-sync",
+ "embassy-sync 0.2.0 (git+https://github.com/embassy-rs/embassy?rev=3e730aa8b06401003202bf9e21a9c83ec6b21b0e)",
 ]
 
 [[package]]
 name = "embassy-embedded-hal"
 version = "0.1.0"
-source = "git+https://github.com/embassy-rs/embassy?rev=5a03b2e9e802626127038cff5634795f576f1c69#5a03b2e9e802626127038cff5634795f576f1c69"
+source = "git+https://github.com/embassy-rs/embassy?rev=3e730aa8b06401003202bf9e21a9c83ec6b21b0e#3e730aa8b06401003202bf9e21a9c83ec6b21b0e"
 dependencies = [
- "embassy-sync",
+ "embassy-sync 0.2.0 (git+https://github.com/embassy-rs/embassy?rev=3e730aa8b06401003202bf9e21a9c83ec6b21b0e)",
  "embedded-hal 0.2.7",
  "embedded-hal 1.0.0-alpha.10",
  "embedded-hal-async",
@@ -494,15 +506,15 @@ dependencies = [
 
 [[package]]
 name = "embassy-executor"
-version = "0.1.1"
-source = "git+https://github.com/embassy-rs/embassy?rev=5a03b2e9e802626127038cff5634795f576f1c69#5a03b2e9e802626127038cff5634795f576f1c69"
+version = "0.2.0"
+source = "git+https://github.com/embassy-rs/embassy?rev=3e730aa8b06401003202bf9e21a9c83ec6b21b0e#3e730aa8b06401003202bf9e21a9c83ec6b21b0e"
 dependencies = [
  "atomic-polyfill 1.0.1",
  "cortex-m",
  "critical-section 1.1.1",
  "defmt",
  "embassy-macros",
- "embassy-time",
+ "embassy-time 0.1.1 (git+https://github.com/embassy-rs/embassy?rev=3e730aa8b06401003202bf9e21a9c83ec6b21b0e)",
  "futures-util",
  "static_cell",
 ]
@@ -510,12 +522,12 @@ dependencies = [
 [[package]]
 name = "embassy-futures"
 version = "0.1.0"
-source = "git+https://github.com/embassy-rs/embassy?rev=5a03b2e9e802626127038cff5634795f576f1c69#5a03b2e9e802626127038cff5634795f576f1c69"
+source = "git+https://github.com/embassy-rs/embassy?rev=3e730aa8b06401003202bf9e21a9c83ec6b21b0e#3e730aa8b06401003202bf9e21a9c83ec6b21b0e"
 
 [[package]]
 name = "embassy-hal-common"
 version = "0.1.0"
-source = "git+https://github.com/embassy-rs/embassy?rev=5a03b2e9e802626127038cff5634795f576f1c69#5a03b2e9e802626127038cff5634795f576f1c69"
+source = "git+https://github.com/embassy-rs/embassy?rev=3e730aa8b06401003202bf9e21a9c83ec6b21b0e#3e730aa8b06401003202bf9e21a9c83ec6b21b0e"
 dependencies = [
  "defmt",
  "num-traits",
@@ -523,8 +535,8 @@ dependencies = [
 
 [[package]]
 name = "embassy-macros"
-version = "0.1.0"
-source = "git+https://github.com/embassy-rs/embassy?rev=5a03b2e9e802626127038cff5634795f576f1c69#5a03b2e9e802626127038cff5634795f576f1c69"
+version = "0.2.0"
+source = "git+https://github.com/embassy-rs/embassy?rev=3e730aa8b06401003202bf9e21a9c83ec6b21b0e#3e730aa8b06401003202bf9e21a9c83ec6b21b0e"
 dependencies = [
  "darling",
  "proc-macro2",
@@ -535,7 +547,7 @@ dependencies = [
 [[package]]
 name = "embassy-net"
 version = "0.1.0"
-source = "git+https://github.com/embassy-rs/embassy?rev=5a03b2e9e802626127038cff5634795f576f1c69#5a03b2e9e802626127038cff5634795f576f1c69"
+source = "git+https://github.com/embassy-rs/embassy?rev=3e730aa8b06401003202bf9e21a9c83ec6b21b0e#3e730aa8b06401003202bf9e21a9c83ec6b21b0e"
 dependencies = [
  "as-slice 0.2.1",
  "atomic-polyfill 1.0.1",
@@ -543,9 +555,9 @@ dependencies = [
  "defmt",
  "embassy-hal-common",
  "embassy-net-driver",
- "embassy-sync",
- "embassy-time",
- "embedded-io",
+ "embassy-sync 0.2.0 (git+https://github.com/embassy-rs/embassy?rev=3e730aa8b06401003202bf9e21a9c83ec6b21b0e)",
+ "embassy-time 0.1.1 (git+https://github.com/embassy-rs/embassy?rev=3e730aa8b06401003202bf9e21a9c83ec6b21b0e)",
+ "embedded-io 0.4.0",
  "embedded-nal-async",
  "futures",
  "generic-array 0.14.6",
@@ -558,7 +570,7 @@ dependencies = [
 [[package]]
 name = "embassy-net-driver"
 version = "0.1.0"
-source = "git+https://github.com/embassy-rs/embassy?rev=5a03b2e9e802626127038cff5634795f576f1c69#5a03b2e9e802626127038cff5634795f576f1c69"
+source = "git+https://github.com/embassy-rs/embassy?rev=3e730aa8b06401003202bf9e21a9c83ec6b21b0e#3e730aa8b06401003202bf9e21a9c83ec6b21b0e"
 dependencies = [
  "defmt",
 ]
@@ -566,17 +578,17 @@ dependencies = [
 [[package]]
 name = "embassy-net-driver-channel"
 version = "0.1.0"
-source = "git+https://github.com/embassy-rs/embassy?rev=5a03b2e9e802626127038cff5634795f576f1c69#5a03b2e9e802626127038cff5634795f576f1c69"
+source = "git+https://github.com/embassy-rs/embassy?rev=3e730aa8b06401003202bf9e21a9c83ec6b21b0e#3e730aa8b06401003202bf9e21a9c83ec6b21b0e"
 dependencies = [
  "embassy-futures",
  "embassy-net-driver",
- "embassy-sync",
+ "embassy-sync 0.2.0 (git+https://github.com/embassy-rs/embassy?rev=3e730aa8b06401003202bf9e21a9c83ec6b21b0e)",
 ]
 
 [[package]]
 name = "embassy-rp"
 version = "0.1.0"
-source = "git+https://github.com/embassy-rs/embassy?rev=5a03b2e9e802626127038cff5634795f576f1c69#5a03b2e9e802626127038cff5634795f576f1c69"
+source = "git+https://github.com/embassy-rs/embassy?rev=3e730aa8b06401003202bf9e21a9c83ec6b21b0e#3e730aa8b06401003202bf9e21a9c83ec6b21b0e"
 dependencies = [
  "atomic-polyfill 1.0.1",
  "cfg-if",
@@ -589,46 +601,87 @@ dependencies = [
  "embassy-executor",
  "embassy-futures",
  "embassy-hal-common",
- "embassy-sync",
- "embassy-time",
+ "embassy-sync 0.2.0 (git+https://github.com/embassy-rs/embassy?rev=3e730aa8b06401003202bf9e21a9c83ec6b21b0e)",
+ "embassy-time 0.1.1 (git+https://github.com/embassy-rs/embassy?rev=3e730aa8b06401003202bf9e21a9c83ec6b21b0e)",
  "embassy-usb-driver",
  "embedded-hal 0.2.7",
  "embedded-hal 1.0.0-alpha.10",
  "embedded-hal-async",
  "embedded-hal-nb",
- "embedded-io",
+ "embedded-io 0.4.0",
  "embedded-storage",
+ "fixed",
  "futures",
  "nb 1.0.0",
  "paste",
  "pio",
  "pio-proc",
  "rand_core",
- "rp2040-pac2",
+ "rp-pac",
 ]
 
 [[package]]
 name = "embassy-sync"
 version = "0.1.0"
-source = "git+https://github.com/embassy-rs/embassy?rev=5a03b2e9e802626127038cff5634795f576f1c69#5a03b2e9e802626127038cff5634795f576f1c69"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b6ea38e6ea5d0361d087680f786c19a1454becb06174790280534a3be05ed839"
+dependencies = [
+ "atomic-polyfill 1.0.1",
+ "cfg-if",
+ "critical-section 1.1.1",
+ "embedded-io 0.3.1",
+ "futures-util",
+ "heapless",
+]
+
+[[package]]
+name = "embassy-sync"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a0dad296a6f70bfdc32ef52442a31f98c28e1608893c1cecc9b6f419bab005a0"
 dependencies = [
  "cfg-if",
  "critical-section 1.1.1",
- "embedded-io",
+ "embedded-io 0.4.0",
+ "futures-util",
+ "heapless",
+]
+
+[[package]]
+name = "embassy-sync"
+version = "0.2.0"
+source = "git+https://github.com/embassy-rs/embassy?rev=3e730aa8b06401003202bf9e21a9c83ec6b21b0e#3e730aa8b06401003202bf9e21a9c83ec6b21b0e"
+dependencies = [
+ "cfg-if",
+ "critical-section 1.1.1",
+ "embedded-io 0.4.0",
  "futures-util",
  "heapless",
 ]
 
 [[package]]
 name = "embassy-time"
-version = "0.1.0"
-source = "git+https://github.com/embassy-rs/embassy?rev=5a03b2e9e802626127038cff5634795f576f1c69#5a03b2e9e802626127038cff5634795f576f1c69"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cd403e218939bba4a1fe4b58c6f81bf0818852bdd824147f95e6dc4ff4166ac4"
 dependencies = [
  "atomic-polyfill 1.0.1",
  "cfg-if",
  "critical-section 1.1.1",
  "defmt",
- "embassy-sync",
+ "embedded-hal 0.2.7",
+ "futures-util",
+ "heapless",
+]
+
+[[package]]
+name = "embassy-time"
+version = "0.1.1"
+source = "git+https://github.com/embassy-rs/embassy?rev=3e730aa8b06401003202bf9e21a9c83ec6b21b0e#3e730aa8b06401003202bf9e21a9c83ec6b21b0e"
+dependencies = [
+ "atomic-polyfill 1.0.1",
+ "cfg-if",
+ "critical-section 1.1.1",
  "embedded-hal 0.2.7",
  "futures-util",
  "heapless",
@@ -637,7 +690,7 @@ dependencies = [
 [[package]]
 name = "embassy-usb-driver"
 version = "0.1.0"
-source = "git+https://github.com/embassy-rs/embassy?rev=5a03b2e9e802626127038cff5634795f576f1c69#5a03b2e9e802626127038cff5634795f576f1c69"
+source = "git+https://github.com/embassy-rs/embassy?rev=3e730aa8b06401003202bf9e21a9c83ec6b21b0e#3e730aa8b06401003202bf9e21a9c83ec6b21b0e"
 dependencies = [
  "defmt",
 ]
@@ -677,6 +730,12 @@ dependencies = [
  "nb 1.0.0",
 ]
 
+[[package]]
+name = "embedded-io"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "33dfba9e6c113f2fd8537c943780a7345945e66c86972e356b1152e19481bcf5"
+
 [[package]]
 name = "embedded-io"
 version = "0.4.0"
@@ -703,7 +762,7 @@ version = "0.4.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "27ce84f518ca912777ec143db235f4d615e3bf8d4e46d507d6ef12daf5b1df98"
 dependencies = [
- "embedded-io",
+ "embedded-io 0.4.0",
  "embedded-nal",
  "heapless",
  "no-std-net 0.6.0",
@@ -754,6 +813,18 @@ dependencies = [
  "libc",
 ]
 
+[[package]]
+name = "fixed"
+version = "1.23.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "79386fdcec5e0fde91b1a6a5bcd89677d1f9304f7f986b154a1b9109038854d9"
+dependencies = [
+ "az",
+ "bytemuck",
+ "half",
+ "typenum",
+]
+
 [[package]]
 name = "fixedbitset"
 version = "0.4.2"
@@ -878,6 +949,15 @@ dependencies = [
  "wasi",
 ]
 
+[[package]]
+name = "half"
+version = "2.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "02b4af3693f1b705df946e9fe5631932443781d0aabb423b62fcd4d73f6d2fd0"
+dependencies = [
+ "crunchy",
+]
+
 [[package]]
 name = "hash32"
 version = "0.2.1"
@@ -1402,9 +1482,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1"
 
 [[package]]
-name = "rp2040-pac2"
-version = "0.1.0"
-source = "git+https://github.com/embassy-rs/rp2040-pac2?rev=017e3c9007b2d3b6965f0d85b5bf8ce3fa6d7364#017e3c9007b2d3b6965f0d85b5bf8ce3fa6d7364"
+name = "rp-pac"
+version = "2.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "75de4bf19a348bd73e49e0476b74a1a19c305de1289c87a7f5049f939246c927"
 dependencies = [
  "cortex-m",
  "cortex-m-rt",
@@ -1615,7 +1696,7 @@ dependencies = [
  "cipher",
  "ctr",
  "digest",
- "embedded-io",
+ "embedded-io 0.4.0",
  "futures",
  "getrandom",
  "heapless",
@@ -1634,6 +1715,23 @@ dependencies = [
  "zeroize",
 ]
 
+[[package]]
+name = "sunset-demo-embassy-common"
+version = "0.1.0"
+dependencies = [
+ "defmt",
+ "embassy-futures",
+ "embassy-net",
+ "embassy-net-driver",
+ "embassy-sync 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "embedded-io 0.4.0",
+ "heapless",
+ "menu",
+ "sunset",
+ "sunset-embassy",
+ "sunset-sshwire-derive",
+]
+
 [[package]]
 name = "sunset-demo-embassy-picow"
 version = "0.1.0"
@@ -1652,11 +1750,11 @@ dependencies = [
  "embassy-net",
  "embassy-net-driver",
  "embassy-rp",
- "embassy-sync",
- "embassy-time",
+ "embassy-sync 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "embassy-time 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "embedded-hal 1.0.0-alpha.10",
  "embedded-hal-async",
- "embedded-io",
+ "embedded-io 0.4.0",
  "getrandom",
  "heapless",
  "menu",
@@ -1666,7 +1764,9 @@ dependencies = [
  "sha2",
  "static_cell",
  "sunset",
+ "sunset-demo-embassy-common",
  "sunset-embassy",
+ "sunset-sshwire-derive",
 ]
 
 [[package]]
@@ -1675,8 +1775,8 @@ version = "0.2.0-alpha"
 dependencies = [
  "atomic-polyfill 1.0.1",
  "embassy-futures",
- "embassy-sync",
- "embedded-io",
+ "embassy-sync 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "embedded-io 0.4.0",
  "log",
  "pin-utils",
  "sunset",
diff --git a/embassy/demos/picow/Cargo.toml b/embassy/demos/picow/Cargo.toml
index 6e6dbf3..0fa6a12 100644
--- a/embassy/demos/picow/Cargo.toml
+++ b/embassy/demos/picow/Cargo.toml
@@ -7,16 +7,21 @@ edition = "2021"
 # blank
 
 [dependencies]
-cyw43 = { git = "https://github.com/embassy-rs/cyw43/", rev = "46efce6ea2d4280d5e8e1eaece53269a94fb4847", features = ["defmt"]}
-cyw43-pio = { git = "https://github.com/embassy-rs/cyw43/", rev = "46efce6ea2d4280d5e8e1eaece53269a94fb4847" }
+sunset-embassy = { path = "../../" }
+sunset = { path = "../../.." }
+sunset-sshwire-derive = { version = "0.1", path = "../../../sshwire-derive" }
+sunset-demo-embassy-common= { path = "../common", features = ["defmt"] }
+
+cyw43 = { git = "https://github.com/embassy-rs/cyw43/", rev = "c19de2984751ba6fa2972ee66cfa2a6310d5f0c1", features = ["defmt"]}
+cyw43-pio = { git = "https://github.com/embassy-rs/cyw43/", rev = "c19de2984751ba6fa2972ee66cfa2a6310d5f0c1" }
 # cyw43 = { path = "/home/matt/3rd/rs/cyw43", features = ["defmt"]}
-embassy-executor = { version = "0.1.0",  features = ["defmt", "integrated-timers", "executor-thread", "arch-cortex-m"] }
-embassy-time = { version = "0.1.0",  features = ["defmt", "defmt-timestamp-uptime"] }
-embassy-rp = { version = "0.1.0",  features = ["defmt", "unstable-traits", "nightly", "unstable-pac", "pio"] }
+embassy-executor = { version = "0.2",  features = ["defmt", "integrated-timers", "executor-thread", "arch-cortex-m"] }
+embassy-time = { version = "0.1",  features = ["defmt", "defmt-timestamp-uptime"] }
+embassy-rp = { version = "0.1.0",  features = ["defmt", "unstable-traits", "nightly", "unstable-pac"] }
 # embassy-net/nightly is required for asynch::Read/Write on TcpReader/TcpWriter
-embassy-net = { version = "0.1.0", features = ["tcp", "dhcpv4", "medium-ethernet", "nightly", "defmt"] }
+embassy-net = { version = "0.1.0", features = ["tcp", "dhcpv4", "medium-ethernet", "nightly"] }
 embassy-net-driver = { version = "0.1.0" }
-embassy-sync = { version = "0.1.0" }
+embassy-sync = { version = "0.2.0" }
 embassy-futures = { version = "0.1.0" }
 atomic-polyfill = "0.1.5"
 static_cell = "1.0"
@@ -33,8 +38,6 @@ embedded-hal-async = { version = "0.2.0-alpha.1" }
 embedded-io = { version = "0.4", features = ["async", "defmt"] }
 heapless = "0.7.15"
 
-sunset-embassy = { path = "../../" }
-sunset = { path = "../../.." }
 getrandom = { version = "0.2", default-features = false, features = ["custom"]}
 pin-utils = "0.1"
 
@@ -47,7 +50,7 @@ rand = { version = "0.8", default-features = false, features = ["getrandom"] }
 sha2 = { version = "0.10", default-features = false }
 
 [features]
-default = ["defmt"]
+default = ["defmt", "sunset-demo-embassy-common/defmt"]
 defmt = []
 
 # Use cyw43 firmware already on flash. This saves time when developing.
@@ -56,15 +59,13 @@ defmt = []
 romfw = []
 
 [patch.crates-io]
-embassy-executor = { git = "https://github.com/embassy-rs/embassy", rev = "5a03b2e9e802626127038cff5634795f576f1c69" }
-embassy-time = { git = "https://github.com/embassy-rs/embassy", rev = "5a03b2e9e802626127038cff5634795f576f1c69" }
-embassy-futures = { git = "https://github.com/embassy-rs/embassy", rev = "5a03b2e9e802626127038cff5634795f576f1c69" }
-embassy-sync = { git = "https://github.com/embassy-rs/embassy", rev = "5a03b2e9e802626127038cff5634795f576f1c69" }
-embassy-rp = { git = "https://github.com/embassy-rs/embassy", rev = "5a03b2e9e802626127038cff5634795f576f1c69" }
-embassy-net = { git = "https://github.com/embassy-rs/embassy", rev = "5a03b2e9e802626127038cff5634795f576f1c69" }
+embassy-executor = { git = "https://github.com/embassy-rs/embassy", rev = "3e730aa8b06401003202bf9e21a9c83ec6b21b0e" }
+embassy-futures = { git = "https://github.com/embassy-rs/embassy", rev = "3e730aa8b06401003202bf9e21a9c83ec6b21b0e" }
+embassy-rp = { git = "https://github.com/embassy-rs/embassy", rev = "3e730aa8b06401003202bf9e21a9c83ec6b21b0e" }
+embassy-net = { git = "https://github.com/embassy-rs/embassy", rev = "3e730aa8b06401003202bf9e21a9c83ec6b21b0e" }
 # for cyw43
-embassy-net-driver-channel = { git = "https://github.com/embassy-rs/embassy", rev = "5a03b2e9e802626127038cff5634795f576f1c69" }
-embassy-net-driver = { git = "https://github.com/embassy-rs/embassy", rev = "5a03b2e9e802626127038cff5634795f576f1c69" }
+embassy-net-driver-channel = { git = "https://github.com/embassy-rs/embassy", rev = "3e730aa8b06401003202bf9e21a9c83ec6b21b0e" }
+embassy-net-driver = { git = "https://github.com/embassy-rs/embassy", rev = "3e730aa8b06401003202bf9e21a9c83ec6b21b0e" }
 
 [profile.dev]
 debug = 2
diff --git a/embassy/demos/picow/src/flash_config.rs b/embassy/demos/picow/src/flash_config.rs
new file mode 100644
index 0000000..0b3828c
--- /dev/null
+++ b/embassy/demos/picow/src/flash_config.rs
@@ -0,0 +1,102 @@
+#[allow(unused_imports)]
+use {
+    sunset::error::{Error, Result, TrapBug},
+};
+
+use defmt::{debug, info, warn, error, trace};
+
+use embassy_rp::flash::{Flash, ERASE_SIZE, FLASH_BASE};
+use embassy_rp::peripherals::FLASH;
+
+use sha2::Digest;
+
+use core::borrow::Borrow;
+
+use sunset_sshwire_derive::*;
+use sunset::sshwire;
+use sunset::sshwire::{BinString, SSHEncode, SSHDecode, WireResult, SSHSource, SSHSink, WireError};
+use sunset::sshwire::OwnOrBorrow;
+
+use crate::demo_common::SSHConfig;
+
+// bump this when the format changes
+const CURRENT_VERSION: u8 = 1;
+
+// TODO: unify offsets with wifi's romfw feature
+const CONFIG_OFFSET: u32 = 0x150000;
+const FLASH_SIZE: usize = 2*1024*1024;
+
+#[derive(SSHEncode, SSHDecode)]
+struct FlashConfig<'a> {
+    version: u8,
+    config: OwnOrBorrow<'a, SSHConfig>,
+    /// sha256 hash of config
+    hash: [u8; 32],
+}
+
+fn config_hash(config: &SSHConfig) -> Result<[u8; 32]> {
+    let mut h = sha2::Sha256::new();
+    sshwire::hash_ser(&mut h, config, None)?;
+    Ok(h.finalize().into())
+}
+
+/// Loads a SSHConfig at startup. Good for persisting hostkeys.
+pub fn load_or_create(flash: &mut Flash<'_, FLASH, FLASH_SIZE>) -> Result<SSHConfig> {
+    let c = load(flash);
+    match load(flash) {
+        Ok(c) => {
+            info!("Good existing config");
+            return Ok(c)
+        }
+        Err(c) => info!("Existing config bad, making new"),
+    }
+
+    let c = SSHConfig::new()?;
+    if let Err(e) = save(flash, &c) {
+        warn!("Error writing config");
+    }
+    Ok(c)
+}
+
+pub fn load(flash: &mut Flash<'_, FLASH, FLASH_SIZE>) -> Result<SSHConfig> {
+    let mut buf = [0u8; ERASE_SIZE];
+    flash.read(CONFIG_OFFSET, &mut buf).map_err(|_| Error::msg("flash error"))?;
+    let s: FlashConfig = sshwire::read_ssh(&buf, None)?;
+
+    if s.version != CURRENT_VERSION {
+        return Err(Error::msg("wrong config version"))
+    }
+
+    let calc_hash = config_hash(s.config.borrow())?;
+    if calc_hash != s.hash {
+        return Err(Error::msg("bad config hash"))
+    }
+
+    if let OwnOrBorrow::Own(c) = s.config {
+        Ok(c)
+    } else {
+        // OK panic - OwnOrBorrow always decodes to Own variant
+        panic!()
+    }
+}
+
+pub fn save(flash: &mut Flash<'_, FLASH, FLASH_SIZE>, config: &SSHConfig) -> Result<()> {
+    let mut buf = [0u8; ERASE_SIZE];
+    let sc = FlashConfig {
+        version: CURRENT_VERSION,
+        config: OwnOrBorrow::Borrow(&config),
+        hash: config_hash(&config)?,
+    };
+    sshwire::write_ssh(&mut buf, &sc)?;
+    trace!("flash erase");
+    flash.erase(CONFIG_OFFSET, CONFIG_OFFSET + ERASE_SIZE as u32)
+    .map_err(|_| Error::msg("flash erase error"))?;
+
+    trace!("flash write");
+    flash.write(CONFIG_OFFSET, &buf)
+    .map_err(|_| Error::msg("flash write error"))?;
+
+    trace!("save done");
+    Ok(())
+}
+
diff --git a/embassy/demos/picow/src/main.rs b/embassy/demos/picow/src/main.rs
index acb0b39..3e3a547 100644
--- a/embassy/demos/picow/src/main.rs
+++ b/embassy/demos/picow/src/main.rs
@@ -2,8 +2,7 @@
 #![no_main]
 #![feature(type_alias_impl_trait)]
 #![feature(async_fn_in_trait)]
-#![allow(incomplete_features)]
-//#![feature(impl_trait_in_assoc_type)]
+// #![allow(incomplete_features)]
 
 use defmt::*;
 use embassy_executor::Spawner;
@@ -21,10 +20,11 @@ use embassy_sync::blocking_mutex::raw::NoopRawMutex;
 use sunset::*;
 use sunset_embassy::SSHServer;
 
+pub(crate) use sunset_demo_embassy_common as demo_common;
+use crate::demo_common::singleton;
+
+mod flash_config;
 mod wifi;
-#[path = "../../common/server.rs"]
-#[macro_use]
-mod demo_common;
 
 use demo_common::{SSHConfig, demo_menu, Shell};
 
@@ -46,16 +46,24 @@ async fn main(spawner: Spawner) {
     caprand::setup(&mut p.PIN_10).unwrap();
     getrandom::register_custom_getrandom!(caprand::getrandom);
 
+    let mut flash = embassy_rp::flash::Flash::new(p.FLASH);
+
+    let config = flash_config::load_or_create(&mut flash).unwrap();
+
+    let ssh_config = &*singleton!(
+        config
+    );
+
     let (_, sm, _, _, _) = p.PIO0.split();
+    let wifi_net = ssh_config.wifi_net.as_str();
+    let wifi_pw = ssh_config.wifi_pw.as_ref().map(|p| p.as_str());
+
     // spawn the wifi stack
-    let stack = wifi::wifi_stack(&spawner, p.PIN_23, p.PIN_24, p.PIN_25, p.PIN_29, p.DMA_CH0, sm).await;
+    let stack = wifi::wifi_stack(&spawner, p.PIN_23, p.PIN_24, p.PIN_25, p.PIN_29, p.DMA_CH0, sm,
+        wifi_net, wifi_pw).await;
     let stack = &*singleton!(stack);
     unwrap!(spawner.spawn(net_task(&stack)));
 
-    let ssh_config = &*singleton!(
-        demo_common::SSHConfig::new().unwrap()
-    );
-
     for _ in 0..NUM_LISTENERS {
         spawner.spawn(listener(&stack, &ssh_config)).unwrap();
     }
diff --git a/embassy/demos/picow/src/wifi.rs b/embassy/demos/picow/src/wifi.rs
index bc7246d..e01c537 100644
--- a/embassy/demos/picow/src/wifi.rs
+++ b/embassy/demos/picow/src/wifi.rs
@@ -15,7 +15,7 @@ use static_cell::StaticCell;
 use rand::rngs::OsRng;
 use rand::RngCore;
 
-use crate::singleton;
+use crate::demo_common::singleton;
 
 #[embassy_executor::task]
 async fn wifi_task(
@@ -32,6 +32,7 @@ async fn wifi_task(
 pub(crate) async fn wifi_stack(spawner: &Spawner,
     p23: PIN_23, p24: PIN_24, p25: PIN_25, p29: PIN_29, dma: DMA_CH0,
     sm: PioStateMachineInstance<Pio0, Sm0>,
+    wifi_net: &str, wpa_password: Option<&str>,
     ) -> embassy_net::Stack<cyw43::NetDriver<'static>>
     {
 
@@ -46,14 +47,13 @@ pub(crate) async fn wifi_stack(spawner: &Spawner,
     spawner.spawn(wifi_task(runner)).unwrap();
 
     control.init(clm).await;
-    // control.set_power_management(cyw43::PowerManagementMode::PowerSave).await;
+    // the default is PowerSave
+    control.set_power_management(cyw43::PowerManagementMode::Performance).await;
 
-    let net = option_env!("WIFI_NETWORK").unwrap_or("guest");
-    let pw = option_env!("WIFI_PASSWORD");
-    if let Some(pw) = pw {
-        control.join_wpa2(net, pw).await;
+    if let Some(pw) = wpa_password {
+        control.join_wpa2(wifi_net, pw).await;
     } else {
-        control.join_open(net).await;
+        control.join_open(wifi_net).await;
     }
 
     let config = embassy_net::Config::Dhcp(Default::default());
diff --git a/embassy/demos/std/Cargo.lock b/embassy/demos/std/Cargo.lock
index 48d2caf..cbbcd40 100644
--- a/embassy/demos/std/Cargo.lock
+++ b/embassy/demos/std/Cargo.lock
@@ -305,7 +305,7 @@ dependencies = [
  "cfg-if",
  "critical-section",
  "embassy-macros",
- "embassy-time",
+ "embassy-time 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "futures-util",
  "log",
  "static_cell",
@@ -314,12 +314,12 @@ dependencies = [
 [[package]]
 name = "embassy-futures"
 version = "0.1.0"
-source = "git+https://github.com/embassy-rs/embassy?rev=5a03b2e9e802626127038cff5634795f576f1c69#5a03b2e9e802626127038cff5634795f576f1c69"
+source = "git+https://github.com/embassy-rs/embassy?rev=3e730aa8b06401003202bf9e21a9c83ec6b21b0e#3e730aa8b06401003202bf9e21a9c83ec6b21b0e"
 
 [[package]]
 name = "embassy-hal-common"
 version = "0.1.0"
-source = "git+https://github.com/embassy-rs/embassy?rev=5a03b2e9e802626127038cff5634795f576f1c69#5a03b2e9e802626127038cff5634795f576f1c69"
+source = "git+https://github.com/embassy-rs/embassy?rev=3e730aa8b06401003202bf9e21a9c83ec6b21b0e#3e730aa8b06401003202bf9e21a9c83ec6b21b0e"
 dependencies = [
  "num-traits",
 ]
@@ -339,15 +339,15 @@ dependencies = [
 [[package]]
 name = "embassy-net"
 version = "0.1.0"
-source = "git+https://github.com/embassy-rs/embassy?rev=5a03b2e9e802626127038cff5634795f576f1c69#5a03b2e9e802626127038cff5634795f576f1c69"
+source = "git+https://github.com/embassy-rs/embassy?rev=3e730aa8b06401003202bf9e21a9c83ec6b21b0e#3e730aa8b06401003202bf9e21a9c83ec6b21b0e"
 dependencies = [
  "as-slice 0.2.1",
  "atomic-polyfill 1.0.1",
  "atomic-pool",
  "embassy-hal-common",
  "embassy-net-driver",
- "embassy-sync",
- "embassy-time",
+ "embassy-sync 0.2.0 (git+https://github.com/embassy-rs/embassy?rev=3e730aa8b06401003202bf9e21a9c83ec6b21b0e)",
+ "embassy-time 0.1.1 (git+https://github.com/embassy-rs/embassy?rev=3e730aa8b06401003202bf9e21a9c83ec6b21b0e)",
  "embedded-io",
  "embedded-nal-async",
  "futures",
@@ -362,12 +362,25 @@ dependencies = [
 [[package]]
 name = "embassy-net-driver"
 version = "0.1.0"
-source = "git+https://github.com/embassy-rs/embassy?rev=5a03b2e9e802626127038cff5634795f576f1c69#5a03b2e9e802626127038cff5634795f576f1c69"
+source = "git+https://github.com/embassy-rs/embassy?rev=3e730aa8b06401003202bf9e21a9c83ec6b21b0e#3e730aa8b06401003202bf9e21a9c83ec6b21b0e"
 
 [[package]]
 name = "embassy-sync"
-version = "0.1.0"
-source = "git+https://github.com/embassy-rs/embassy?rev=5a03b2e9e802626127038cff5634795f576f1c69#5a03b2e9e802626127038cff5634795f576f1c69"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a0dad296a6f70bfdc32ef52442a31f98c28e1608893c1cecc9b6f419bab005a0"
+dependencies = [
+ "cfg-if",
+ "critical-section",
+ "embedded-io",
+ "futures-util",
+ "heapless",
+]
+
+[[package]]
+name = "embassy-sync"
+version = "0.2.0"
+source = "git+https://github.com/embassy-rs/embassy?rev=3e730aa8b06401003202bf9e21a9c83ec6b21b0e#3e730aa8b06401003202bf9e21a9c83ec6b21b0e"
 dependencies = [
  "cfg-if",
  "critical-section",
@@ -378,19 +391,32 @@ dependencies = [
 
 [[package]]
 name = "embassy-time"
-version = "0.1.0"
-source = "git+https://github.com/embassy-rs/embassy?rev=5a03b2e9e802626127038cff5634795f576f1c69#5a03b2e9e802626127038cff5634795f576f1c69"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cd403e218939bba4a1fe4b58c6f81bf0818852bdd824147f95e6dc4ff4166ac4"
 dependencies = [
  "atomic-polyfill 1.0.1",
  "cfg-if",
  "critical-section",
- "embassy-sync",
  "embedded-hal 0.2.7",
  "futures-util",
  "heapless",
  "log",
 ]
 
+[[package]]
+name = "embassy-time"
+version = "0.1.1"
+source = "git+https://github.com/embassy-rs/embassy?rev=3e730aa8b06401003202bf9e21a9c83ec6b21b0e#3e730aa8b06401003202bf9e21a9c83ec6b21b0e"
+dependencies = [
+ "atomic-polyfill 1.0.1",
+ "cfg-if",
+ "critical-section",
+ "embedded-hal 0.2.7",
+ "futures-util",
+ "heapless",
+]
+
 [[package]]
 name = "embedded-hal"
 version = "0.2.7"
@@ -1053,6 +1079,23 @@ dependencies = [
  "zeroize",
 ]
 
+[[package]]
+name = "sunset-demo-embassy-common"
+version = "0.1.0"
+dependencies = [
+ "embassy-futures",
+ "embassy-net",
+ "embassy-net-driver",
+ "embassy-sync 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "embedded-io",
+ "heapless",
+ "log",
+ "menu",
+ "sunset",
+ "sunset-embassy",
+ "sunset-sshwire-derive",
+]
+
 [[package]]
 name = "sunset-demo-embassy-std"
 version = "0.1.0"
@@ -1064,8 +1107,8 @@ dependencies = [
  "embassy-futures",
  "embassy-net",
  "embassy-net-driver",
- "embassy-sync",
- "embassy-time",
+ "embassy-sync 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "embassy-time 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "embedded-hal 1.0.0-alpha.9",
  "embedded-hal-async",
  "embedded-io",
@@ -1078,6 +1121,7 @@ dependencies = [
  "sha2",
  "static_cell",
  "sunset",
+ "sunset-demo-embassy-common",
  "sunset-embassy",
 ]
 
@@ -1087,7 +1131,7 @@ version = "0.2.0-alpha"
 dependencies = [
  "atomic-polyfill 1.0.1",
  "embassy-futures",
- "embassy-sync",
+ "embassy-sync 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "embedded-io",
  "log",
  "pin-utils",
@@ -1308,8 +1352,3 @@ dependencies = [
  "syn",
  "synstructure",
 ]
-
-[[patch.unused]]
-name = "embassy-executor"
-version = "0.1.1"
-source = "git+https://github.com/embassy-rs/embassy?rev=5a03b2e9e802626127038cff5634795f576f1c69#5a03b2e9e802626127038cff5634795f576f1c69"
diff --git a/embassy/demos/std/Cargo.toml b/embassy/demos/std/Cargo.toml
index ef3dd5e..af7cf4a 100644
--- a/embassy/demos/std/Cargo.toml
+++ b/embassy/demos/std/Cargo.toml
@@ -7,12 +7,12 @@ edition = "2021"
 # blank
 
 [dependencies]
-embassy-executor = { version = "0.1.0",  features = ["log", "std", "nightly", "integrated-timers"] }
-embassy-time = { version = "0.1.0",  features = ["log", "std"] }
+embassy-executor = { version = "0.1.0",  default-features=false, features = ["log", "std", "nightly", "integrated-timers"] }
+embassy-time = { version = "0.1",  default-features=false, features = ["log", "std"] }
 # embassy-net/nightly is required for asynch::Read/Write on TcpReader/TcpWriter
-embassy-net = { version = "0.1.0", features = ["log", "tcp", "dhcpv4", "medium-ethernet", "nightly"] }
+embassy-net = { version = "0.1.0", features = ["tcp", "dhcpv4", "medium-ethernet", "nightly"] }
 embassy-net-driver = { version = "0.1.0" }
-embassy-sync = { version = "0.1.0" }
+embassy-sync = { version = "0.2.0" }
 embassy-futures = { version = "0.1.0" }
 atomic-polyfill = "0.1.5"
 static_cell = "1.0"
@@ -22,7 +22,7 @@ env_logger = "0.9.0"
 
 embedded-hal-1 = { package = "embedded-hal", version = "1.0.0-alpha.8" }
 embedded-hal-async = { version = "0.1.0-alpha.2" }
-embedded-io = { version = "0.4", features = ["async"] }
+embedded-io = { version = "0.4", default-features=false, features = ["async"] }
 heapless = "0.7.15"
 
 # for tuntap
@@ -33,19 +33,17 @@ menu = "0.3"
 
 sunset-embassy = { path = "../../" }
 sunset = { path = "../../.." }
+sunset-demo-embassy-common= { path = "../common", features = ["log"] }
 
 critical-section = "1.1"
 rand = { version = "0.8", default-features = false, features = ["getrandom"] }
 sha2 = { version = "0.10", default-features = false }
 
 [patch.crates-io]
-embassy-executor = { git = "https://github.com/embassy-rs/embassy", rev = "5a03b2e9e802626127038cff5634795f576f1c69" }
-embassy-time = { git = "https://github.com/embassy-rs/embassy", rev = "5a03b2e9e802626127038cff5634795f576f1c69" }
-embassy-futures = { git = "https://github.com/embassy-rs/embassy", rev = "5a03b2e9e802626127038cff5634795f576f1c69" }
-embassy-sync = { git = "https://github.com/embassy-rs/embassy", rev = "5a03b2e9e802626127038cff5634795f576f1c69" }
-embassy-net = { git = "https://github.com/embassy-rs/embassy", rev = "5a03b2e9e802626127038cff5634795f576f1c69" }
+embassy-futures = { git = "https://github.com/embassy-rs/embassy", rev = "3e730aa8b06401003202bf9e21a9c83ec6b21b0e" }
+embassy-net = { git = "https://github.com/embassy-rs/embassy", rev = "3e730aa8b06401003202bf9e21a9c83ec6b21b0e" }
 # for tuntap
-embassy-net-driver = { git = "https://github.com/embassy-rs/embassy", rev = "5a03b2e9e802626127038cff5634795f576f1c69" }
+embassy-net-driver = { git = "https://github.com/embassy-rs/embassy", rev = "3e730aa8b06401003202bf9e21a9c83ec6b21b0e" }
 
 [profile.dev]
 debug = 2
diff --git a/embassy/demos/std/src/main.rs b/embassy/demos/std/src/main.rs
index 06d9d4b..43a5f5f 100644
--- a/embassy/demos/std/src/main.rs
+++ b/embassy/demos/std/src/main.rs
@@ -25,10 +25,10 @@ use sunset::*;
 use sunset_embassy::SSHServer;
 
 mod tuntap;
-#[path = "../../common/server.rs"]
-mod demo_common;
+pub(crate) use sunset_demo_embassy_common as demo_common;
+use crate::demo_common::singleton;
 
-use demo_common::{SSHConfig, demo_menu, Shell, BufOutput};
+use demo_common::{SSHConfig, demo_menu, Shell};
 
 const NUM_LISTENERS: usize = 2;
 // +1 for dhcp
@@ -62,7 +62,7 @@ async fn main_task(spawner: Spawner) {
     spawner.spawn(net_task(stack)).unwrap();
 
     let ssh_config = &*singleton!(
-        demo_common::SSHConfig::new().unwrap()
+        SSHConfig::new().unwrap()
     );
 
     for _ in 0..NUM_LISTENERS {
@@ -90,7 +90,7 @@ impl Shell for DemoShell {
             let mut stdio = serv.stdio(chan_handle).await?;
 
             let mut menu_buf = [0u8; 64];
-            let menu_out = BufOutput::default();
+            let menu_out = demo_menu::BufOutput::default();
 
             let mut menu = MenuRunner::new(&demo_menu::ROOT_MENU, &mut menu_buf, menu_out);
 
diff --git a/src/behaviour.rs b/src/behaviour.rs
index 3e17703..207f818 100644
--- a/src/behaviour.rs
+++ b/src/behaviour.rs
@@ -203,7 +203,8 @@ pub trait ServBehaviour {
     // then later request a single key.
     // Also could make it take a closure to call with the key, lets it just
     // be loaded on the stack rather than kept in memory for the whole lifetime.
-    fn hostkeys(&mut self) -> BhResult<&[sign::SignKey]>;
+    // TODO: a slice of references is a bit awkward?
+    fn hostkeys(&mut self) -> BhResult<&[&sign::SignKey]>;
 
     #[allow(unused)]
     // TODO: or return a slice of enums
@@ -313,7 +314,7 @@ impl CliBehaviour for UnusedCli {
 #[derive(Debug)]
 pub struct UnusedServ;
 impl ServBehaviour for UnusedServ {
-    fn hostkeys(&mut self) -> BhResult<&[sign::SignKey]> {
+    fn hostkeys(&mut self) -> BhResult<&[&sign::SignKey]> {
         unreachable!()
     }
     fn open_session(&mut self, chan: ChanHandle) -> channel::ChanOpened {
diff --git a/src/kex.rs b/src/kex.rs
index 4ba22af..6c523eb 100644
--- a/src/kex.rs
+++ b/src/kex.rs
@@ -785,12 +785,12 @@ mod tests {
         sshwire::packet_from_bytes(&out_buf[..l], &ctx).unwrap()
     }
 
-    struct TestServBehaviour {
-        keys: Vec<SignKey>,
+    struct TestServBehaviour<'a> {
+        keys: Vec<&'a SignKey>,
     }
 
-    impl ServBehaviour for TestServBehaviour {
-        fn hostkeys(&mut self) -> BhResult<&[SignKey]> {
+    impl<'a> ServBehaviour for TestServBehaviour<'a> {
+        fn hostkeys(&mut self) -> BhResult<&[&'a SignKey]> {
             Ok(self.keys.as_slice())
         }
 
@@ -908,6 +908,7 @@ mod tests {
 
         let mut keys = vec![];
         keys.push(crate::SignKey::generate(crate::KeyType::Ed25519, None).unwrap());
+        let keys: Vec<&SignKey> = keys.iter().collect();
         let mut sb = TestServBehaviour {
             keys,
         };
diff --git a/src/packets.rs b/src/packets.rs
index dc31909..a5a16a3 100644
--- a/src/packets.rs
+++ b/src/packets.rs
@@ -298,7 +298,7 @@ pub struct UserauthBanner<'a> {
 #[sshwire(variant_prefix)]
 pub enum PubKey<'a> {
     #[sshwire(variant = SSH_NAME_ED25519)]
-    Ed25519(Ed25519PubKey<'a>),
+    Ed25519(Ed25519PubKey),
 
     #[cfg(feature = "rsa")]
     #[sshwire(variant = SSH_NAME_RSA)]
@@ -344,8 +344,7 @@ impl TryFrom<&PubKey<'_>> for ssh_key::PublicKey {
     fn try_from(k: &PubKey) -> Result<Self> {
         match k {
             PubKey::Ed25519(e) => {
-                let eb: &[u8; 32] = e.key.0.try_into().map_err(|_| Error::BadKey)?;
-                Ok(ssh_key::public::Ed25519PublicKey(*eb).into())
+                Ok(ssh_key::public::Ed25519PublicKey(e.key.0).into())
             }
 
             #[cfg(feature = "rsa")]
@@ -363,15 +362,14 @@ impl TryFrom<&PubKey<'_>> for ssh_key::PublicKey {
 }
 
 #[derive(Debug, Clone, PartialEq, SSHEncode, SSHDecode)]
-pub struct Ed25519PubKey<'a> {
-    pub key: BinString<'a>,
+pub struct Ed25519PubKey {
+    pub key: Blob<[u8; 32]>,
 }
 
-impl TryFrom<&Ed25519PubKey<'_>> for salty::PublicKey {
+impl TryFrom<&Ed25519PubKey> for salty::PublicKey {
     type Error = Error;
     fn try_from(k: &Ed25519PubKey) -> Result<Self> {
-        let b: [u8; 32] = k.key.0.try_into().map_err(|_| Error::BadKey)?;
-        (&b).try_into().map_err(|_| Error::BadKey)
+        Ok((&k.key.0).try_into().map_err(|_| Error::BadKey)?)
     }
 }
 
@@ -1102,7 +1100,7 @@ mod tests {
             method: AuthMethod::PubKey(MethodPubKey {
                 sig_algo: "something",
                 pubkey: Blob(PubKey::Ed25519(
-                    Ed25519PubKey { key: BinString(b"zzzz") }
+                    Ed25519PubKey { key: Blob([3u8; 32]) }
                 )),
                 sig: Some(Blob(Signature::Ed25519(Ed25519Sig {
                     sig: BinString(b"sighere")
diff --git a/src/sign.rs b/src/sign.rs
index f748f0d..ef00bb0 100644
--- a/src/sign.rs
+++ b/src/sign.rs
@@ -14,7 +14,7 @@ use crate::*;
 use packets::ParseContext;
 use sshnames::*;
 use packets::{PubKey, Signature, Ed25519PubKey};
-use sshwire::{BinString, SSHEncode};
+use sshwire::{BinString, SSHEncode, Blob};
 
 use pretty_hex::PrettyHex;
 
@@ -79,7 +79,7 @@ impl SigType {
         match (self, pubkey, sig) {
 
             (SigType::Ed25519, PubKey::Ed25519(k), Signature::Ed25519(s)) => {
-                let k: &[u8; 32] = k.key.0.try_into().map_err(|_| Error::BadKey)?;
+                let k: &[u8; 32] = &k.key.0;
                 let k: salty::PublicKey = k.try_into().map_err(|_| Error::BadKey)?;
                 let s: &[u8; 64] = s.sig.0.try_into().map_err(|_| Error::BadSig)?;
                 let s: salty::Signature = s.into();
@@ -170,8 +170,8 @@ pub enum KeyType {
 /// or potentially send the signing requests to an SSH agent or other entity.
 #[derive(ZeroizeOnDrop)]
 pub enum SignKey {
-    // TODO bloat: this is an expanded keypair, we should store the raw bytes
-    Ed25519(salty::Keypair),
+    // 32 byte seed value is the private key
+    Ed25519([u8; 32]),
 
     #[zeroize(skip)]
     AgentEd25519(salty::PublicKey),
@@ -194,8 +194,8 @@ impl SignKey {
                     return Err(Error::msg("Bad key size"));
                 }
                 let mut seed = [0u8; 32];
-                random::fill_random(seed.as_mut_slice())?;
-                Ok(Self::Ed25519((&seed).into()))
+                random::fill_random(&mut seed)?;
+                Ok(Self::Ed25519(seed))
             },
 
             #[cfg(feature = "rsa")]
@@ -220,11 +220,14 @@ impl SignKey {
 
     pub fn pubkey(&self) -> PubKey {
         match self {
-            SignKey::Ed25519(k) => PubKey::Ed25519(Ed25519PubKey
-                { key: BinString(k.public.as_bytes()) } ),
+            SignKey::Ed25519(seed) => {
+                let k = salty::Keypair::from(seed);
+                PubKey::Ed25519(Ed25519PubKey
+                { key: Blob(k.public.as_bytes().clone()) } )
+            },
 
             SignKey::AgentEd25519(pk) => PubKey::Ed25519(Ed25519PubKey
-                { key: BinString(pk.as_bytes()) } ),
+                { key: Blob(pk.as_bytes().clone()) } ),
 
 
             #[cfg(feature = "rsa")]
@@ -277,7 +280,8 @@ impl SignKey {
 
     pub(crate) fn sign(&self, msg: &impl SSHEncode, parse_ctx: Option<&ParseContext>) -> Result<OwnedSig> {
         let sig: OwnedSig = match self {
-            SignKey::Ed25519(k) => {
+            SignKey::Ed25519(seed) => {
+                let k = salty::Keypair::from(seed);
                 let sig = k.sign_parts(|h| {
                     sshwire::hash_ser(h, msg, parse_ctx).map_err(|_| salty::Error::ContextTooLong)
                 })
@@ -349,11 +353,7 @@ impl TryFrom<ssh_key::PrivateKey> for SignKey {
     fn try_from(k: ssh_key::PrivateKey) -> Result<Self> {
         match k.key_data() {
             ssh_key::private::KeypairData::Ed25519(k) => {
-                let key = salty::Keypair {
-                    secret: (&k.private.to_bytes()).into(),
-                    public: (&k.public.0).try_into().map_err(|_| Error::BadKey)?,
-                };
-                Ok(SignKey::Ed25519(key))
+                Ok(SignKey::Ed25519(k.private.to_bytes()))
             }
 
             #[cfg(feature = "rsa")]
diff --git a/src/sshwire.rs b/src/sshwire.rs
index aaaabe4..1d4b60e 100644
--- a/src/sshwire.rs
+++ b/src/sshwire.rs
@@ -162,6 +162,8 @@ pub fn hash_ser_length(hash_ctx: &mut impl DigestUpdate,
 }
 
 /// Hashes the SSH wire format representation of `value`
+///
+/// Will only fail if `value.enc()` can return an error.
 pub fn hash_ser(hash_ctx: &mut impl DigestUpdate,
     value: &dyn SSHEncode,
     parse_ctx: Option<&ParseContext>,
@@ -383,6 +385,7 @@ impl<'de> SSHDecode<'de> for TextString<'de> {
 }
 
 /// A wrapper for a `u32` length prefixed data structure `B`, such as a public key blob
+#[derive(PartialEq, Clone)]
 pub struct Blob<B>(pub B);
 
 impl<B> AsRef<B> for Blob<B> {
@@ -391,12 +394,6 @@ impl<B> AsRef<B> for Blob<B> {
     }
 }
 
-impl<B: Clone> Clone for Blob<B> {
-    fn clone(&self) -> Self {
-        Blob(self.0.clone())
-    }
-}
-
 impl<B: SSHEncode + Debug> Debug for Blob<B> {
     fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
         if let Ok(len) = sshwire::length_enc(&self.0) {
@@ -479,6 +476,12 @@ impl<const N: usize> SSHEncode for &[u8; N] {
     }
 }
 
+impl<const N: usize> SSHEncode for [u8; N] {
+    fn enc(&self, s: &mut dyn SSHSink) -> WireResult<()> {
+        s.push(self.as_slice())
+    }
+}
+
 impl SSHEncode for &str {
     fn enc(&self, s: &mut dyn SSHSink) -> WireResult<()> {
         let v = self.as_bytes();
@@ -567,6 +570,14 @@ impl<'de, const N: usize> SSHDecode<'de> for &'de [u8; N] {
     }
 }
 
+impl<'de, const N: usize> SSHDecode<'de> for [u8; N] {
+    fn dec<S>(s: &mut S) -> WireResult<Self>
+    where S: SSHSource<'de> {
+        // OK unwrap: take() fails if the length is short
+        Ok(s.take(N)?.try_into().unwrap())
+    }
+}
+
 /// Like `digest::DynDigest` but simpler.
 ///
 /// Doesn't have any optional methods that depend on `alloc`.
@@ -623,6 +634,37 @@ impl<'de> SSHDecode<'de> for rsa::BigUint {
     }
 }
 
+// TODO: is there already something like this?
+pub enum OwnOrBorrow<'a, T> {
+    Own(T),
+    Borrow(&'a T),
+}
+
+impl<T: SSHEncode> SSHEncode for OwnOrBorrow<'_, T> {
+    fn enc(&self, s: &mut dyn SSHSink) -> WireResult<()> {
+        match self {
+            Self::Own(t) => t.enc(s),
+            Self::Borrow(t) => t.enc(s),
+        }
+    }
+}
+
+impl<'de, T: SSHDecode<'de>> SSHDecode<'de> for OwnOrBorrow<'_, T> {
+    fn dec<S>(s: &mut S) -> WireResult<Self> where S: SSHSource<'de> {
+        Ok(Self::Own(T::dec(s)?))
+    }
+}
+
+impl<'a, T> core::borrow::Borrow<T> for OwnOrBorrow<'a, T> {
+    fn borrow(&self) -> &T {
+        match self {
+            Self::Own(t) => &t,
+            Self::Borrow(t) => t,
+        }
+    }
+}
+
+
 #[cfg(test)]
 pub(crate) mod tests {
     use crate::*;
@@ -709,7 +751,7 @@ pub(crate) mod tests {
         let p = Userauth60::PkOk(UserauthPkOk {
             algo: "ed25519",
             key: Blob(PubKey::Ed25519(Ed25519PubKey {
-                key: BinString(&[0x11, 0x22, 0x33]),
+                key: Blob([0x11; 32]),
             })),
         }).into();
         ctx.cli_auth_type = Some(auth::AuthType::PubKey);