Rust · Python · MIT
AWS Session Manager,
as a library.
Open sessions, stream bytes and forward ports from inside your own async application. No session-manager-plugin binary, no subprocess, no scraping stdout.
cargo add aws-ssm-bridgeorpip install aws-ssm-bridgeuse aws_ssm_bridge::SessionBuilder;
use futures_util::StreamExt;
let session = SessionBuilder::new("i-0123456789abcdef0").start().await?;
session.wait_ready().await?;
let mut output = session.output();
session.send(&b"uname -a\r"[..]).await?;
while let Some(chunk) = output.next().await {
print!("{}", String::from_utf8_lossy(&chunk));
}
session.terminate().await?;import asyncio
from aws_ssm_bridge import SessionManager
async def main():
manager = await SessionManager.new()
async with await manager.start_session("i-0123456789abcdef0") as session:
await session.send(b"uname -a\r")
async for chunk in session.output():
print(chunk.decode(errors="replace"), end="")
asyncio.run(main())What you get
Shell & command sessions
Interactive shells, plus both AWS-Start*Command documents, with typed wrappers so a wrong parameter name is a compile error.
Port forwarding
smux-multiplexed, so many concurrent TCP connections share one WebSocket without blocking or corrupting each other.
KMS session encryption
AES-256-GCM end-to-end for accounts that mandate it. A client that cannot negotiate it fails the handshake rather than downgrading.
Interactive terminal
Raw byte passthrough, SIGWINCH resize, and terminal restoration on every exit path — panics included.
Reconnection
An output stream that outlives the session beneath it, rebuilt with full-jitter backoff when — and only when — a retry could help.
Python bindings
The full async API with type stubs and context managers, shipped as an abi3 wheel for CPython 3.8 and later.
One guarantee everything else rests on
A session is either running or closed. Every way it can end — a clean terminate(), the agent hanging up, a dead network, a protocol violation — resolves closed() and records a CloseReason.
That is what makes the layers above it work: the port forwarder stops accepting when the tunnel dies, the pool reaps dead entries, and reconnection knows when to rebuild. There is no state in which the handle looks alive but nothing is running.
tokio::select! {
() = session.closed() => {
eprintln!("gone: {}", session.close_reason().unwrap());
}
result = do_work(&session) => result?,
}Built to be verified, not trusted
unsafe_code is forbidden crate-wideThe data channel refuses any endpoint that is not an AWS SSM messages host, and the session token travels only in the open message — never in a URL, where proxies and traces would record it. Read the threat model
Start here
Getting started
Credentials, your first session, port forwarding, and the errors you will actually hit.
Wire protocol
What travels between a client and the SSM agent, and the traps that make a session hang silently.
Python API
The full async surface, with type stubs, context managers and fan-out patterns.
Not affiliated with AWS. This is an independent implementation of a protocol documented by observation, not endorsed or sponsored by Amazon Web Services, Inc.