$_ bashkit

SSH Support

Bashkit provides ssh, scp, and sftp builtins for remote command execution and file transfer over SSH. The default transport uses russh.

See also: specs/ssh-support.md

Quick Start

use bashkit::{Bash, SshConfig};

# #[tokio::main]
# async fn main() -> bashkit::Result<()> {
let mut bash = Bash::builder()
    .ssh(SshConfig::new().allow("supabase.sh"))
    .build();

let result = bash.exec("ssh supabase.sh").await?;
# Ok(())
# }

Usage

# Remote command
ssh host.example.com 'uname -a'

# Heredoc
ssh host.example.com <<'EOF'
psql -c 'SELECT version()'
EOF

# Shell session (TUI services like supabase.sh)
ssh supabase.sh

# SCP
scp local.txt host.example.com:/remote/path.txt
scp host.example.com:/remote/file.txt local.txt

# SFTP (heredoc/pipe mode)
sftp host.example.com <<'EOF'
put /tmp/data.csv /var/import/data.csv
get /var/export/report.csv /tmp/report.csv
ls /var/import
EOF

Configuration

use bashkit::SshConfig;
use std::time::Duration;

let config = SshConfig::new()
    .allow("*.supabase.co")           // wildcard subdomain
    .allow("bastion.example.com")     // exact host
    .allow_port(2222)                 // additional port (default: 22 only)
    .default_user("deploy")           // when no user@ prefix
    .timeout(Duration::from_secs(30)) // connection timeout
    .max_response_bytes(10_000_000)   // max output size
    .max_sessions(5);                 // concurrent session limit

Authentication

Tried in order: none (public services) โ†’ public key (-i flag or default_private_key()) โ†’ password (default_password()).

Security

  • Default-deny host allowlist with glob patterns and port restrictions
  • Keys read from VFS only, never host ~/.ssh/
  • Remote paths shell-escaped (TM-SSH-008)
  • Response size and session count limits