SSH

SSH gives you an interactive shell on a machine.

dedalus ssh <machine_id>

SSH requests can be made for machines that are sleeping or awake. If a machine is asleep, Dedalus will wake the machine for you and open the connection once the machine and SSH tunnel are ready. New SSH access waits for an explicit sleep to finish, then wakes the machine. SSH is unavailable after a machine is destroyed.

Session lifetime

Once connected, the session closes after 30 minutes without SSH activity or after 12 hours total, whichever comes first. Activity is defined as any SSH traffic in either direction.

SSH session lifetime is independent of the machine's lifecycle. If a machine sleeps during an active SSH session, that session will close.

If a machine is configured with autosleep, the SSH session is treated as active work. The machine will not autosleep until the session closes, even if no other activity occurs on the machine.

Connect from the SDK

Use the SDK when your application needs to control the SSH connection itself. Generate an ephemeral key pair, keep the private key in your application, and pass the OpenSSH-formatted public key to the SDK.

import time

from dedalus_sdk import Dedalus

# Requests SSH access, waits for the machine to wake, and returns its connection credentials.
def authorize_ssh(client: Dedalus, machine_id: str, public_key: str):
    session = client.machines.ssh.create(
        machine_id=machine_id,
        public_key=public_key,
    )
    for _ in range(120):
        if session.status != "wake_in_progress":
            break
        time.sleep((session.retry_after_ms or 500) / 1000)
        session = client.machines.ssh.retrieve(
            machine_id=machine_id,
            session_id=session.session_id,
        )
    if session.status == "wake_in_progress":
        raise TimeoutError("SSH session did not become ready")

    connection = session.connection
    if session.status != "ready" or connection is None:
        raise RuntimeError(session.error_message or f"SSH session is {session.status}")
    if connection.user_certificate is None or connection.host_trust is None:
        raise RuntimeError("SSH session is ready without connection credentials")
    return connection
import Dedalus from "dedalus";

const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));

// Requests SSH access, waits for the machine to wake, and returns its connection credentials.
async function authorizeSSH(
  client: Dedalus,
  machineId: string,
  publicKey: string,
) {
  let session = await client.machines.ssh.create({
    machine_id: machineId,
    public_key: publicKey,
  });
  for (let attempt = 0; session.status === "wake_in_progress" && attempt < 120; attempt++) {
    await sleep(session.retry_after_ms || 500);
    session = await client.machines.ssh.retrieve({
      machine_id: machineId,
      session_id: session.session_id,
    });
  }
  if (session.status === "wake_in_progress") {
    throw new Error("SSH session did not become ready");
  }

  const connection = session.connection;
  if (session.status !== "ready" || !connection) {
    throw new Error(session.error_message || `SSH session is ${session.status}`);
  }
  if (!connection.user_certificate || !connection.host_trust) {
    throw new Error("SSH session is ready without connection credentials");
  }
  return connection;
}
package sshclient

import (
    "context"
    "fmt"
    "time"

    "github.com/dedalus-labs/dedalus-go"
)

// authorizeSSH requests SSH access, waits for the machine to wake, and
// returns its connection credentials.
func authorizeSSH(
    ctx context.Context,
    client dedalus.Client,
    machineID string,
    publicKey string,
) (dedalus.SSHConnection, error) {
    session, err := client.Machines.SSH.New(ctx, dedalus.MachineSSHNewParams{
        MachineID: machineID,
        SSHSessionCreateParams: dedalus.SSHSessionCreateParams{
            PublicKey: publicKey,
        },
    })
    if err != nil {
        return dedalus.SSHConnection{}, err
    }

    for attempt := 0; session.Status == dedalus.SSHSessionStatusWakeInProgress; attempt++ {
        if attempt == 120 {
            return dedalus.SSHConnection{}, fmt.Errorf("ssh session did not become ready")
        }
        delay := 500 * time.Millisecond
        if session.RetryAfterMs > 0 {
            delay = time.Duration(session.RetryAfterMs) * time.Millisecond
        }
        select {
        case <-ctx.Done():
            return dedalus.SSHConnection{}, ctx.Err()
        case <-time.After(delay):
        }
        session, err = client.Machines.SSH.Get(ctx, dedalus.MachineSSHGetParams{
            MachineID: machineID,
            SessionID: session.SessionID,
        })
        if err != nil {
            return dedalus.SSHConnection{}, err
        }
    }

    if session.Status != dedalus.SSHSessionStatusReady {
        return dedalus.SSHConnection{}, fmt.Errorf(
            "SSH session is %s: %s",
            session.Status,
            session.ErrorMessage,
        )
    }
    if session.Connection.UserCertificate == "" || session.Connection.HostTrust.PublicKey == "" {
        return dedalus.SSHConnection{}, fmt.Errorf("ssh session is ready without connection credentials")
    }
    return session.Connection, nil
}

The returned connection contains everything needed to configure an SSH client. Write connection.user_certificate as the OpenSSH CertificateFile. Write connection.host_trust to UserKnownHostsFile as an @cert-authority entry, using its host_pattern and public_key. Then connect to connection.endpoint on connection.port as connection.ssh_username, using the private key that matches the public key you submitted.

Set IdentitiesOnly=yes, GlobalKnownHostsFile=/dev/null, and StrictHostKeyChecking=yes so OpenSSH uses only the temporary identity and Dedalus host authority. Delete the private key, certificate, and host-trust files when the connection closes.

Raw API

The CLI handles key generation and connection setup for you. The raw Machines API requires an OpenSSH public key. Generated SDK resource methods mirror that endpoint and require the same field.

Parameters

machine_id · string · required

The machine to open SSH access to.

public_key · string · required

An OpenSSH-formatted public key. The dedalus ssh command generates and submits this value automatically.