diff --git a/ziti/src/main/kotlin/org/openziti/impl/ChannelImpl.kt b/ziti/src/main/kotlin/org/openziti/impl/ChannelImpl.kt
index 6568f339..0dd76636 100644
--- a/ziti/src/main/kotlin/org/openziti/impl/ChannelImpl.kt
+++ b/ziti/src/main/kotlin/org/openziti/impl/ChannelImpl.kt
@@ -125,7 +125,8 @@ internal class ChannelImpl(val addr: String, val id: Identity, val apiSession: (
                 chState.value = Channel.State.Connecting
                 val jobs = mutableListOf<Deferred<Unit>>()
                 try {
-                    peer = Transport.dial(addr, id.sslContext(), CONNECT_TIMEOUT)
+                    peer = Transport.dial(addr, id.sslContext(), CONNECT_TIMEOUT, EDGE_APP_PROTOCOL)
+                    d{ "connected with ${peer.applicationProtocol()}"}
                     jobs += async { txer(peer) }
                     jobs += async { rxer(peer) }
 
@@ -310,5 +311,6 @@ internal class ChannelImpl(val addr: String, val id: Identity, val apiSession: (
 
     companion object {
         const val CONNECT_TIMEOUT: Long = 20_000
+        const val EDGE_APP_PROTOCOL = "ziti-edge"
     }
 }
diff --git a/ziti/src/main/kotlin/org/openziti/net/Transport.kt b/ziti/src/main/kotlin/org/openziti/net/Transport.kt
index 72a54c6b..8ec541a1 100644
--- a/ziti/src/main/kotlin/org/openziti/net/Transport.kt
+++ b/ziti/src/main/kotlin/org/openziti/net/Transport.kt
@@ -35,30 +35,46 @@ import java.nio.channels.SocketChannel
 import java.util.concurrent.CancellationException
 import java.util.concurrent.CompletableFuture
 import javax.net.ssl.SSLContext
+import javax.net.ssl.SSLEngine
 
 internal interface Transport : Closeable {
 
     companion object {
-        suspend fun dial(address: String, ssl: SSLContext, timeout: Long): Transport {
+        suspend fun dial(address: String, ssl: SSLContext, timeout: Long, vararg protos: String): Transport {
             val url = URI.create(address)
-            val tls = TLS(url.host, url.port, ssl)
+            val tls = TLS(url.host, url.port, ssl, *protos)
             tls.connect(timeout)
             return tls
         }
     }
 
+    fun applicationProtocol(): String?
     fun isClosed(): Boolean
     suspend fun connect(timeout: Long)
 
     suspend fun write(buf: ByteBuffer)
     suspend fun read(buf: ByteBuffer, full: Boolean = true): Int
 
-    class TLS(host: String, port: Int, val sslContext: SSLContext) : Transport {
+    class TLS(host: String, port: Int, sslContext: SSLContext, vararg appProto: String) : Transport {
         lateinit var socket: AsynchronousTlsChannel
-        val addr = InetSocketAddress(InetAddress.getByName(host), port)
+        private val addr = InetSocketAddress(InetAddress.getByName(host), port)
+        private val sslEngine: SSLEngine
+
+        init {
+            val sslParms = sslContext.defaultSSLParameters
+            sslParms.applicationProtocols = appProto
+
+            sslEngine = sslContext.createSSLEngine(host, port).apply {
+                sslParameters = sslParms
+                useClientMode = true
+            }
+        }
+
+        override fun applicationProtocol(): String? = sslEngine.applicationProtocol
 
         override suspend fun connect(timeout: Long) {
-            val connOp = connectAsync(addr, sslContext)
+            val connOp = connectAsync(addr, sslEngine)
+            sslEngine.handshakeStatus
             socket = kotlin.runCatching {
                 withTimeout(timeout) {
                     connOp.await()
@@ -98,19 +114,19 @@ internal interface Transport : Closeable {
         companion object {
             val asyncGroup = AsynchronousTlsChannelGroup()
 
-            internal fun connectAsync(address: InetSocketAddress, ssl: SSLContext): Deferred<AsynchronousTlsChannel> {
+            internal fun connectAsync(address: InetSocketAddress, ssl: SSLEngine): Deferred<AsynchronousTlsChannel> {
                 val deferred = CompletableDeferred<AsynchronousTlsChannel>()
                 val sockCh = SocketChannel.open()
-                val sslEngine = ssl.createSSLEngine(address.hostString, address.port).apply {
-                    useClientMode = true
-                }
+
                 val f = CompletableFuture.supplyAsync {
                     sockCh.connect(address)
-                    sockCh.configureBlocking(false)
-                    val tlsCh = ClientTlsChannel.newBuilder(sockCh, SSLEngineWrapper(sslEngine))
+                    val tlsCh = ClientTlsChannel.newBuilder(sockCh, ssl)
                         .withEncryptedBufferAllocator(HeapBufferAllocator())
                         .withPlainBufferAllocator(HeapBufferAllocator())
                         .build()
+                    tlsCh.handshake()
+
+                    sockCh.configureBlocking(false)
                     AsynchronousTlsChannel(asyncGroup, tlsCh, sockCh)
                 }
                 f.whenComplete { ch, ex ->
diff --git a/ziti/src/test/kotlin/org/openziti/net/TransportTest.kt b/ziti/src/test/kotlin/org/openziti/net/TransportTest.kt
index dcaa6659..0f390f7e 100644
--- a/ziti/src/test/kotlin/org/openziti/net/TransportTest.kt
+++ b/ziti/src/test/kotlin/org/openziti/net/TransportTest.kt
@@ -52,6 +52,35 @@ User-Agent: ziti/1.0.2
         }
     }
 
+    @Test(timeout = 65000)
+    fun testALPN() {
+        runBlocking {
+            val t = Transport.dial("tls://google.com:443",
+                SSLContext.getDefault(), 61_000, "http/1.1", "foo", "bar")
+
+            val applicationProtocol = t.applicationProtocol()
+            assertThat("alpn match", "http/1.1" == applicationProtocol)
+
+            val req = """GET /robots.txt HTTP/1.1
+Accept: */*
+Connection: keep-alive
+Host: google.com
+User-Agent: ziti/1.0.2
+
+"""
+            t.write(StandardCharsets.UTF_8.encode(req))
+            val respBuf = ByteBuffer.allocate(1024)
+            t.read(respBuf, false)
+
+            respBuf.flip()
+            val resp = StandardCharsets.UTF_8.decode(respBuf)
+            println(resp)
+            val lines = resp.toString().reader().readLines()
+            assertThat(lines[0], CoreMatchers.startsWith("HTTP/1.1"))
+            t.close()
+        }
+    }
+
     @Test(timeout = 20000, expected = SocketTimeoutException::class)
     fun testCancel() {
         runBlocking {