aws-ssm-bridge

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-bridge
use 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?;

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.

How it is put together

tokio::select! {
    () = session.closed() => {
        eprintln!("gone: {}", session.close_reason().unwrap());
    }
    result = do_work(&session) => result?,
}

Built to be verified, not trusted

forbidunsafe_code is forbidden crate-wide
FuzzedEvery parser that touches network bytes
VerifiedSHA-256 payload digests, like the reference plugin
PinnedWire format locked by byte-offset tests

The 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

Not affiliated with AWS. This is an independent implementation of a protocol documented by observation, not endorsed or sponsored by Amazon Web Services, Inc.