PySSM Client
Enhanced Python AWS SSM Session Manager client with interactive sessions, exec, and file transfer support. It speaks the same binary protocol as the official Go plugin and provides additional functionality like library imports and extended CLI commands.
Highlights:
- Interactive SSH-like sessions via SSM (
sshsubcommand) - TCP port forwarding through SSM (
port-forwardsubcommand) - Direct connect to an existing SSM data channel (
connectsubcommand) - Proper ack/seq handling, terminal raw mode, signal forwarding (Ctrl-C/Z/), and periodic resize updates
- Minimal logging by default; verbose traces with
-v
Installation
Install from PyPI:
pip install pyssm-client
Or with uv:
uv add pyssm-client
Requirements
- Python 3.12+
- AWS credentials discoverable via environment or
~/.aws/credentials
Quick Start
# Interactive SSH-like session
pyssm ssh --target i-0123456789abcdef0
# Port forward to a remote host through an instance
pyssm port-forward --target i-0123456789abcdef0 --remote-port 3306 \
--remote-host mydb.cluster-xxx.rds.amazonaws.com
# Execute single command
pyssm exec --target i-0123456789abcdef0 --command "ls -la"
# Copy files
pyssm copy ./local-file.txt i-0123456789abcdef0:/tmp/remote-file.txt
CLI Usage
The CLI provides five subcommands: connect, ssh, port-forward, exec, and copy.
ssh: Start an SSM session to a target
Starts a new SSM session using boto3 and connects interactively.
pyssm ssh \
--target i-0123456789abcdef0 \
--region us-west-2 \
--profile myprofile
Options:
--target(required): EC2 instance ID (i-*) or managed instance ID (mi-*)--document-name: SSM document (defaults to the agent’s standard shell doc)--parameters: JSON object of document parameters--profile,--region,--endpoint-url: AWS settings forboto3
Global options (apply to all commands):
-v/--verbose: enable DEBUG logging--log-file PATH: log to file in addition to stderr--coalesce-input [auto|on|off](defaultauto): input batchingauto: enabled for non‑TTY stdin (piped input), disabled for interactive TTYson: always enable (tunable with--coalesce-delay-ms)off: always disabled
--coalesce-delay-ms FLOAT(default 10.0): coalescing delay when enabled
Behavior:
- Terminal set to cbreak,
-echo -isig(no double echo; signals forwarded to remote) - Periodic terminal size updates (500ms) and on resize (SIGWINCH)
- Ctrl-C (0x03), Ctrl-\ (0x1c), Ctrl-Z (0x1a) forwarded to remote
exitcleanly closes the session and the CLI
port-forward: TCP port forwarding via SSM
Forward a local TCP port to a remote port through an SSM session. Supports both direct instance port forwarding and remote host forwarding (e.g., to an RDS endpoint reachable from the target instance).
# Forward local port 9090 to port 8080 on the instance
pyssm port-forward --target i-0123456789abcdef0 \
--remote-port 8080 --local-port 9090
# Auto-assign local port, forward to port 3306 on the instance
pyssm port-forward --target i-0123456789abcdef0 --remote-port 3306
# Forward to an RDS endpoint through the instance
pyssm port-forward --target i-0123456789abcdef0 \
--remote-port 3306 --remote-host mydb.cluster-xxx.us-east-1.rds.amazonaws.com
Options:
--target(required): EC2 instance ID (i-*) or managed instance ID (mi-*)--remote-port(required): Port on the remote host to forward to--local-port(default: 0): Local port to listen on (0 for auto-assign)--remote-host: Remote host to forward to through the instance (enables remote host forwarding)--profile,--region,--endpoint-url: AWS settings forboto3
The command automatically selects the appropriate SSM document:
AWS-StartPortForwardingSessionfor direct instance port forwardingAWS-StartPortForwardingSessionToRemoteHostwhen--remote-hostis specified
Port forwarding uses the smux v1 multiplexing protocol for TCP connections over the SSM data channel.
exec: Execute a single command
Execute a single command on a target instance and return the results with proper exit codes.
pyssm exec \
--target i-0123456789abcdef0 \
--command "ls -la /tmp" \
--region us-west-2 \
--profile myprofile
Options:
--target(required): EC2 instance ID (i-*) or managed instance ID (mi-*)--command(required): Shell command to execute--timeout: Command timeout in seconds (default: 600)--profile,--region,--endpoint-url: AWS settings forboto3
This command:
- Executes the command and captures stdout/stderr separately
- Returns the actual exit code of the executed command
- Filters shell noise (prompts, command echoes) from output
- Useful for scripting and automation
copy: File transfer via SSM
Transfer files to/from targets using base64 encoding over SSM sessions.
# Upload local file to remote
pyssm copy \
./local-file.txt i-0123456789abcdef0:/tmp/remote-file.txt \
--region us-west-2 \
--profile myprofile
# Download remote file to local
pyssm copy \
i-0123456789abcdef0:/tmp/remote-file.txt ./local-file.txt \
--region us-west-2 \
--profile myprofile
Options:
--verify-checksum(default: on): Verify file integrity using MD5/SHA256--encoding(default: base64): Transfer encoding (base64, raw, uuencode)--chunk-size: Transfer chunk size in bytes (default: 8192)--profile,--region,--endpoint-url: AWS settings forboto3
Features:
- Automatic checksum verification to ensure file integrity
- Progress reporting during transfer
- Support for binary files via base64 encoding
- Works with any file size (chunked transfer)
connect: Attach to an existing SSM data channel
Connects using session parameters you already have (typical when called by AWS CLI). You can pass a single JSON blob or individual flags.
JSON form (mimics the AWS CLI invocation):
pyssm connect '{
"SessionId": "dacort-abc123",
"StreamUrl": "wss://ssmmessages.us-west-2.amazonaws.com/v1/data-channel/dacort-abc123?...",
"TokenValue": "...",
"target": "i-0123456789abcdef0",
"sessionType": "Standard_Stream"
}'
Flag form:
pyssm connect \
--session-id dacort-abc123 \
--stream-url wss://... \
--token-value ... \
--target i-0123456789abcdef0 \
--session-type Standard_Stream
Notes:
- The CLI validates parameters and will emit friendly errors if missing or invalid.
- Global logging and coalescing options work here too.
Using as a Library
You can embed the plugin in your own Python program. There are four convenient levels: file transfer, exec API, high-level (CLI coordinator), and low-level (session + data channel).
File Transfer API: Upload/download files
For programmatic file transfers with progress tracking and verification:
from pyssm_client.file_transfer import FileTransferClient
from pyssm_client.file_transfer.types import FileTransferOptions
client = FileTransferClient()
# Upload file
options = FileTransferOptions(verify_checksum=True)
success = await client.upload_file(
local_path="./local-file.txt",
remote_path="/tmp/remote-file.txt",
target="i-0123456789abcdef0",
options=options,
region="us-west-2",
profile="myprofile"
)
# Download file
success = await client.download_file(
remote_path="/tmp/remote-file.txt",
local_path="./downloaded-file.txt",
target="i-0123456789abcdef0",
options=options,
region="us-west-2",
profile="myprofile"
)
Exec API: Single command execution
For simple command execution with clean stdout/stderr separation:
from pyssm_client.exec import run_command, run_command_sync
# Async version
result = await run_command(
target="i-0123456789abcdef0",
command="ls -la /tmp",
region="us-west-2",
profile="myprofile"
)
print(f"Exit code: {result.exit_code}")
print(f"Stdout: {result.stdout.decode('utf-8')}")
print(f"Stderr: {result.stderr.decode('utf-8')}")
# Sync version
result = run_command_sync(
target="i-0123456789abcdef0",
command="sha256sum /path/to/file"
)
if result.exit_code == 0:
checksum = result.stdout.decode('utf-8').strip().split()[0]
High-level: reuse the CLI coordinator
import asyncio
from pyssm_client.cli.types import ConnectArguments
from pyssm_client.cli.main import SessionManagerPlugin
from pyssm_client.utils.logging import setup_logging
setup_logging() # or logging.DEBUG for verbose
args = ConnectArguments(
session_id="dacort-abc123",
stream_url="wss://...",
token_value="...",
target="i-0123456789abcdef0",
session_type="Standard_Stream",
)
plugin = SessionManagerPlugin()
exit_code = asyncio.run(plugin.run_session(args))
Low-level: wire session + data channel yourself
import asyncio
from pyssm_client.session.session_handler import SessionHandler
from pyssm_client.communicator.utils import create_websocket_config
from pyssm_client.communicator.data_channel import SessionDataChannel
async def main():
handler = SessionHandler()
# Create session (without executing) from your already-resolved params
session = await handler.validate_input_and_create_session({
"sessionId": "dacort-abc123",
"streamUrl": "wss://...",
"tokenValue": "...",
"target": "i-0123456789abcdef0",
"sessionType": "Standard_Stream",
})
# Create data channel and wire handlers
ws_cfg = create_websocket_config(stream_url="wss://...", token="...")
dc = SessionDataChannel(ws_cfg)
dc.set_input_handler(lambda b: sys.stdout.buffer.write(b) or sys.stdout.flush())
# Optional: closed and coalescing handlers
dc.set_closed_handler(lambda: print("\n[session closed]\n"))
# dc.set_coalescing(True, delay_sec=0.01) # for piped input
session.set_data_channel(dc)
await session.execute() # open websocket + handshake
# then run your own event loop + stdin handling as needed
asyncio.run(main())
Tips:
- For custom UIs, provide your own
input_handler(bytes from the agent) and feedsend_input_data()with bytes from your UI. - Use
send_terminal_size(cols, rows)whenever your layout changes. - On shutdown, call
await session.terminate_session().
Development
Run tests:
uv run pytest -q
Formatting & linting:
uv run black .
uv run ruff check .
uv run mypy .
Logging:
- By default, logs are minimal. Use
-vor set upsetup_logging(level=logging.DEBUG)programmatically for detailed traces.
Architecture
The codebase has been recently refactored for better maintainability and reliability:
- Clean message handling: AWS SSM protocol messages are parsed by dedicated
MessageParserand routed to specialized handlers (HandshakeHandler,StreamHandler,ControlHandler) - Reliable file transfers: Fixed line ending normalization issues that caused checksum mismatches
- Modular design: Session management, WebSocket communication, and CLI coordination are cleanly separated
- Type safety: Full mypy type checking with modern Python type hints
This provides a solid foundation for extending functionality while maintaining compatibility with the AWS SSM protocol.
Known Limitations
- KMS encryption handshake is not implemented; sessions requiring encryption will not negotiate keys.
- InteractiveCommands sessions are stubbed and not fully implemented.
Metadata
Release files for pyssm-client 0.2.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| pyssm_client-0.2.0.tar.gz | 121.2 kB | Without provenance | Details for pyssm_client-0.2.0.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| pyssm_client-0.2.0-py3-none-any.whl | Python 3 | none | any | Details for pyssm_client-0.2.0-py3-none-any.whl |
Total release size: 193.1 kB
Release files / pyssm_client-0.2.0.tar.gz
| Download URL | pyssm_client-0.2.0.tar.gz |
|---|---|
| Size | 121.2 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
7e23b8ff779fe76491757517e722bb7d9b2ce06013c60c52cf7cb64cf661edff
|
|
BLAKE2b-256 checksum How to use checksums |
acc864fe81366911632f95a5871ec8b0589d19c78d8129a302fe331fda436163
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.10.10 {"installer":{"name":"uv","version":"0.10.10","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Release files / pyssm_client-0.2.0-py3-none-any.whl
| Download URL | pyssm_client-0.2.0-py3-none-any.whl |
|---|---|
| Size | 71.9 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
c5906ae4b8bf59d7da966a27175906abe00fe708ea4bfd449699fe3007e83784
|
|
BLAKE2b-256 checksum How to use checksums |
bf7487b94c1060fa4b2e219ffa378bcd8b226808c65fd0a13c2c3396312b8705
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.10.10 {"installer":{"name":"uv","version":"0.10.10","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Security for pyssm-client 0.2.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.