Document Dosh Rust SDK examples

This commit is contained in:
DuProcess
2026-06-30 19:21:28 -04:00
parent bbf6ffd725
commit 13f8cb07c5
3 changed files with 158 additions and 2 deletions
+71 -2
View File
@@ -7,6 +7,7 @@ It runs a `dosh-server` on the remote machine and a `dosh` client locally. The
first setup can use SSH. After that, Dosh can attach over encrypted UDP with first setup can use SSH. After that, Dosh can attach over encrypted UDP with
cached credentials, keep terminal sessions alive, reconnect after network cached credentials, keep terminal sessions alive, reconnect after network
changes, and forward TCP ports. changes, and forward TCP ports.
It also includes native encrypted file copy over the Dosh stream layer.
## Support ## Support
@@ -15,8 +16,8 @@ changes, and forward TCP ports.
- Windows: client only - Windows: client only
- UDP port: one configured server port, default `50000` - UDP port: one configured server port, default `50000`
Dosh is not an SCP/SFTP client and does not implement X11 forwarding. Windows Dosh does not implement SFTP, X11 forwarding, or a Windows server. Windows is
is client-only. client-only.
## Install ## Install
@@ -44,6 +45,12 @@ irm https://git.palav.dev/Palav/dosh/raw/branch/main/install.ps1 | iex
dosh setup HOST # import SSH config and trust the Dosh host key dosh setup HOST # import SSH config and trust the Dosh host key
dosh HOST # connect dosh HOST # connect
dosh HOST COMMAND # connect and run a command dosh HOST COMMAND # connect and run a command
dosh exec HOST COMMAND # run a non-interactive remote command
dosh cp SRC DST # copy files; use host:path for a remote side
dosh ls host:path # list a remote path
dosh cat host:path # print a remote file
dosh mkdir host:path # create a remote directory
dosh rm [-r] host:path # remove a remote file or directory
dosh update # update Dosh dosh update # update Dosh
dosh status HOST # show remote tmux sessions and Dosh service status dosh status HOST # show remote tmux sessions and Dosh service status
dosh restart HOST # restart dosh-server and show service status dosh restart HOST # restart dosh-server and show service status
@@ -56,12 +63,74 @@ Examples:
```sh ```sh
dosh server dosh server
dosh server tm dosh server tm
dosh exec server 'uname -a'
dosh cp file.txt server:tmp/file.txt
dosh cp -r server:Projects/app ./app
dosh cp -r server:Projects/app backup:app
dosh ls server:Projects
dosh cat server:tmp/file.txt
dosh forward server -L 8080:127.0.0.1:80 dosh forward server -L 8080:127.0.0.1:80
dosh forward server -D 1080 dosh forward server -D 1080
dosh forward server -R 2222:127.0.0.1:22 dosh forward server -R 2222:127.0.0.1:22
``` ```
Agent forwarding is opt-in with `-A` and must be enabled on the server. Agent forwarding is opt-in with `-A` and must be enabled on the server.
File copy is enabled by `allow_file_transfer = true` on the server.
## Library
Dosh can also be used as a Rust transport for application protocols that need
encrypted streams, roaming, reconnect behavior, keepalives, retransmission,
flow control, and in-order delivery.
Use `dosh::client::DoshClient` and `dosh::server::DoshServer` for the complete
native-auth path. Use `dosh::transport::DoshTransport` or `StreamMux` only when
you already have your own session/auth layer.
Client-side SDK:
```rust
let client = dosh::client::DoshClient::load()?;
let mut dosh = client
.connect("server")
.service("myapp")
.connect()
.await?
.into_transport();
let stream = dosh.open_service("myapp").await?;
dosh.send(stream, b"hello").await?;
```
Server-side SDK:
```rust
let config = dosh::server::DoshServerConfig::default()
.service("myapp")?;
let mut server = dosh::server::DoshServer::bind(config).await?;
loop {
match server.recv().await? {
dosh::server::DoshServerEvent::Accepted(client) => {
eprintln!("accepted {:?}", client.conn_id);
}
dosh::server::DoshServerEvent::Session { conn_id, event } => {
if let dosh::transport::SessionEvent::Stream(
dosh::transport::TransportEvent::Open(open)
) = event {
server.accept_stream(conn_id, open.stream_id).await?;
}
}
dosh::server::DoshServerEvent::Ignored => {}
}
}
```
The server runtime owns UDP reads and demuxes packets by connection id, so
multiple clients can share one Dosh port without per-session readers racing.
Runnable SDK examples live in `examples/sdk_echo_server.rs` and
`examples/sdk_echo_client.rs`.
## Config ## Config
+50
View File
@@ -0,0 +1,50 @@
use anyhow::{Result, bail};
use dosh::client::DoshClient;
use dosh::transport::{SessionEvent, TransportEvent};
#[tokio::main]
async fn main() -> Result<()> {
let mut args = std::env::args().skip(1);
let host = args.next().unwrap_or_else(|| "server".to_string());
let message = args.collect::<Vec<_>>().join(" ");
let message = if message.is_empty() {
"hello from dosh".to_string()
} else {
message
};
let client = DoshClient::load()?;
let mut transport = client
.connect(host)
.service("echo")
.connect()
.await?
.into_transport();
let stream = transport.open_service("echo").await?;
loop {
match transport.recv().await? {
SessionEvent::Stream(TransportEvent::OpenOk { stream_id, .. })
if stream_id == stream =>
{
transport.send(stream, message.as_bytes().to_vec()).await?;
}
SessionEvent::Stream(TransportEvent::Data(data)) if data.stream_id == stream => {
for chunk in data.chunks {
print!("{}", String::from_utf8_lossy(&chunk));
}
println!();
transport.close(stream).await?;
return Ok(());
}
SessionEvent::Stream(TransportEvent::OpenReject { reason, .. }) => {
bail!("server rejected echo stream: {reason}");
}
SessionEvent::Stream(_)
| SessionEvent::Ping
| SessionEvent::Pong
| SessionEvent::Ignored => {}
}
transport.maintenance().await?;
}
}
+37
View File
@@ -0,0 +1,37 @@
use anyhow::Result;
use dosh::server::{DoshServer, DoshServerConfig, DoshServerEvent};
use dosh::transport::{SessionEvent, TransportEvent};
#[tokio::main]
async fn main() -> Result<()> {
let config = DoshServerConfig::default().service("echo")?;
let mut server = DoshServer::bind(config).await?;
eprintln!("listening on {}", server.local_addr()?);
loop {
match server.recv().await? {
DoshServerEvent::Accepted(client) => {
eprintln!(
"accepted user={} session={} conn={:?}",
client.user, client.session, client.conn_id
);
}
DoshServerEvent::Session {
conn_id,
event: SessionEvent::Stream(TransportEvent::Open(open)),
} => {
server.accept_stream(conn_id, open.stream_id).await?;
}
DoshServerEvent::Session {
conn_id,
event: SessionEvent::Stream(TransportEvent::Data(data)),
} => {
for chunk in data.chunks {
server.send(conn_id, data.stream_id, chunk).await?;
}
}
DoshServerEvent::Session { .. } | DoshServerEvent::Ignored => {}
}
server.maintenance().await?;
}
}