Skip to main content

Open a shell in a running job

Logs tell you what a job chose to print. Sometimes you need to look at the running process itself — what is on disk, what the environment actually contains, whether the thing you expected to be listening is listening.

Runtime SSH gives you a shell inside one of your own running jobs. In the Console it lives under Integrations (/settings/integrations), not as its own rail item.

Preview

Runtime SSH through the Liskov relay is available on Developer and above. It is a Preview capability: the behaviour below is supported, but the relay that carries managed sessions runs on a single machine. If that machine is lost or restarted, every open session drops until it returns; reconnect when it does. Your jobs keep running and are unaffected when that happens. If the processor relaunches the job's sandbox (it does this under memory pressure), managed SSH reconnects on its own, usually within a minute, under a new host key; see Connect below. A helper that stops cleanly, or an SSH sidecar that keeps failing, still ends managed SSH for that job until the job's next run; the job itself is unaffected.

Interactive administration is included. Traffic through the relay counts against your plan's included log volume, and bytes above it are charged to your Service Credits at the plan's log overage rate. Your usage and remaining allowance appear in Billing before any charge is made.

Connecting over your own Tailscale network instead of the relay is a separate capability, available on Pro and above when a live policy version can name it. It is listed as Roadmap in Integrations today — see Bring your own Tailscale network.

Before you start​

This page explains access to existing V4 jobs and their stored key lists. New V4 imports and publications are refused with v4_publication_disabled; you cannot add a key or change provider by publishing another V4 manifest. For new setup or policy changes, use the retained V5 Managed Runtime SSH path.

You need:

  • a job that is currently running — check with proof liskov application status APP_REF;
  • an SSH key pair you control. Liskov never sees the private half;
  • the public half declared in your Application manifest; and
  • proof-cli-liskov installed, signed in, with your organization selected.

If you do not have a key yet:

ssh-keygen -t ed25519 -f ~/.ssh/liskov-runtime -C "liskov runtime ssh"

Keep the private file readable only by you. Liskov reads only the public half, from your manifest.

1. Declare who may connect​

For an existing V4 job, read the public keys in its stored manifest. Access is bound to that published manifest, not a console toggle. The historical shape is:

{
"ingress": {
"ssh": {
"mode": "required",
"provider": {
"kind": "liskov",
"authorizedKeys": [
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAI... liskov runtime ssh"
]
}
}
}
}

"kind": "liskov" uses the Liskov-operated relay, which needs nothing from you beyond the key. To use your own Tailscale network instead, see Bring your own Tailscale network.

Only a key already authorized by the job's published manifest can connect. Registering another key does not change that list. New authorization uses the V5 path; V4 publication is closed.

Name the key in your organization (optional)​

Registering the public half in your organization's operator-key registry gives it a name and a fingerprint you can see in the console under Settings → Runtime SSH and with proof liskov runtime-ssh operator-key list --json:

proof liskov runtime-ssh operator-key add --name work-laptop --identity ~/.ssh/liskov-runtime

Registering a key does not grant access: the manifest above is still exactly who may connect. Removing one with operator-key remove does withdraw its access, everywhere in your organization, whether or not a manifest still lists it; see Take someone's access away. Full command reference: CLI reference.

The next CLI release will let you connect with proof liskov ssh APP_REF --key work-laptop, or omit the key flag when exactly one authorized local key matches. This is not yet in the published CLI: continue using --identity below until that release. The name selects your organization's registered public key, and the CLI finds its matching private key directly in ~/.ssh. A public-key file alone cannot open a session. See selection and missing-key errors.

2. Check the connection before you use it​

--print-command resolves and verifies everything without opening a session or consuming an access ticket. Run it first — it is the cheapest way to confirm the job is reachable.

proof liskov ssh APP_REF \
--identity ~/.ssh/liskov-runtime \
--print-command --json

You get the attachment, the deployment and job it is bound to, the host fingerprint, your key fingerprint, and the verified digests of the helper and SSH server Liskov placed in the runtime. No ticket is issued.

3. Connect​

proof liskov ssh APP_REF --identity ~/.ssh/liskov-runtime

The first connection to a job shows its host key and asks you to confirm it. Accept it once and it is pinned; a later mismatch is refused rather than re-prompted. The one exception is a relaunched sandbox: it starts with a new host key, Liskov reports the key it replaced, and CLI 0.16.0 and later say the job's runtime restarted, show both fingerprints and ask you to confirm again. Only Liskov vouches for the new key, as on first use. A second prompt that does not name a restart is a warning, not a formality. Add --accept-host-key to trust the first key, or a restart's new key, without prompting in scripts; it never accepts any other mismatch.

You get an ordinary interactive shell. Your customer process keeps running alongside you.

Pick an exact target with --deployment or --job when an Application has more than one running job.

Forward traffic through the managed session​

With proof-cli-liskov 0.17.0 or later, proof liskov ssh accepts local forwarding (-L) and dynamic forwarding (-D) through the Liskov-managed session. Both flags are repeatable. The client accepts any destination host, with no destination allowlist; the job must still be able to reach that host and port.

For a service listening on port 8080 inside the job, open a port on your machine that forwards to it:

proof liskov ssh APP_REF --identity ~/.ssh/liskov-runtime \
-L 127.0.0.1:18080:127.0.0.1:8080 -N

The syntax is -L [bind:]port:host:hostport: bind and port name the listener on your machine; host and hostport name the destination reached from the job. Here the destination 127.0.0.1 is the job's loopback address. While the command is running, open http://127.0.0.1:18080 on your machine to verify that it returns your service's response.

For a local SOCKS proxy whose connections go out from the job:

proof liskov ssh APP_REF --identity ~/.ssh/liskov-runtime \
-D 127.0.0.1:1080 -N

The syntax is -D [bind:]port. Configure your local client to use the SOCKS proxy at 127.0.0.1:1080, then request a destination the job can reach to verify it. These examples bind only on your machine's loopback address.

-N requests no remote command, so the connection carries forwards without opening a shell. Omit it if you also want an interactive shell. Stop the connection with Ctrl-C when finished. Remote forwarding (-R) is refused. Forwarded bytes count against the same log allowance and Service Credit overage rate as other traffic through the relay.

A job keeps the helper it started with. An older helper that still disables local forwarding with Dropbear -j refuses both -L and -D until that job starts again on a helper that allows forwarding. Upgrading your CLI does not change the helper in a running job.

Verify it worked​

Inside the session:

tty # /dev/pts/0 — you have a real terminal
uname -m # aarch64 — you are on the processor, not your laptop

Then, back on your machine, confirm the access was recorded:

proof liskov application activity APP_REF

Every connection appears in your activity feed — access granted, session opened, and session closed with its duration and how much data moved. That record exists so you can audit access to your own runtimes, including access by Liskov support.

What you can run in there​

The runtime is a minimal Debian image plus your application. Core tools are present: ls, cat, grep, find, sed, awk, tar, ps-less process inspection through /proc, and pidof.

Some familiar tools are not installed, including ps, top, curl, wget, and editors. To list processes:

for p in /proc/[0-9]*; do pid=${p#/proc/}; printf "%-6s %s\n" "$pid" \
"$(tr '\0' ' ' < $p/cmdline 2>/dev/null | cut -c1-60)"; done

Do not install packages. The runtime's contents are digest-verified, and installing at runtime breaks that guarantee for the rest of the job's life. If you need a tool permanently, review the V5 path and its supported runtime-image boundary before preparing a new release.

Take someone's access away​

Withdrawing a key takes effect immediately and needs no publish. It works on the key's fingerprint, so it covers a key that is only in a manifest and was never registered.

ssh-keygen -lf ./departed.pub # prints the SHA256:... fingerprint
proof liskov runtime-ssh withdrawn-key add \
--fingerprint SHA256:... \
--reason "left the team"

If the key is registered in your organization, proof liskov runtime-ssh operator-key remove KEY_ID does the same thing as part of removing it. If you hold the private key yourself, --identity FILE names it instead of --fingerprint.

From that moment, on every Application in the organization:

  • new connection requests and new tickets for that key are refused with runtime_ssh_operator_key_withdrawn;
  • its unused tickets are revoked, and the response says how many; and
  • a session that is already open is not cut. It drains: it ends when that person disconnects, when the job ends, or at the relay's two-hour maximum session duration, whichever comes first. If it must end sooner, end the job.

Withdrawal covers sessions through the Liskov relay. A job on your own Tailscale network is reached through your tailnet, and access there is controlled by Tailscale, not by Liskov.

Verify it:

proof liskov runtime-ssh withdrawn-key list --json

The withdrawal is listed with its ID, and the activity feed records it with the number of tickets revoked. A stored V4 authorizedKeys list is unchanged: V4 cannot be republished to edit it. The withdrawal keeps working even when the stored manifest lists the key, and stays in force for jobs launched later.

To let the key back in, lift the withdrawal with proof liskov runtime-ssh withdrawn-key remove WITHDRAWAL_ID. That only lifts the block: the key must still be in the published manifest to connect.

End one job's access instead of one person's​

Withdrawal is per key, across every Application. To end access to a single job for everyone who has it, open Console Integrations → Liskov-Managed Runtime SSH, find the exact job under Attachments, and select Revoke. The confirmation names the whole-attachment blast radius and the drain below.

The same operation is available from the CLI:

proof liskov runtime-ssh attachment list --json
proof liskov runtime-ssh attachment revoke ATTACHMENT_ID

No new connection request, ticket or relay registration is granted for it, and its unused tickets are revoked; the response reports how many. Revoking an attachment that is already revoked is reported as such rather than failing, so a retried script is safe. The job itself is untouched — its process, health reporting and schedule carry on.

A session already open drains here too, on exactly the same bounds as a withdrawal. Nothing terminates an established relay; if a session must end sooner, end the job.

Bring your own Tailscale network​

Pro and above, as a Preview. On Free or Developer, publishing a manifest that names the Tailscale provider is refused with runtime_ssh_provider_plan_required; the relay path above stays available.

If you already run Tailscale, you can have the job join your own tailnet instead of using the Liskov relay. Traffic then goes directly between your machine and the job over your network, and Liskov is not in the path, so none of it counts against your log volume.

The stored V4 shape names an organization Tailscale integration as follows. It is historical syntax, not a new setup path: V4 import and publication are closed, and Tailscale remains Roadmap until a live policy version can name it.

{
"ingress": {
"ssh": {
"mode": "required",
"provider": { "kind": "tailscale", "integrationId": "int-...", "port": 22 }
}
}
}

An integration belongs to exactly one organization and cannot be used by another.

When something is wrong​

runtime_ssh_attachment_not_ready — the job has not finished preparing SSH, or it has already ended. Check it is still running with proof liskov application status APP_REF. A job that reached its scheduled end is gone; launch a new one. When the error carries a failureCode, that names the specific cause.

runtime_ssh_attachment_not_ready with failureCode: operator_revoked — an administrator of your organization revoked this attachment deliberately. Retrying will not help, and neither will another key: the revocation covers the whole attachment. A new attachment is created when a new job launches.

A host-key mismatch warning — stop. The key pinned on your machine does not match the job answering. Do not override it. Re-run with --print-command --json and compare the fingerprint against the activity feed for that job. A job whose sandbox was relaunched answers with a new key: CLI 0.16.0 and later recognise that case and ask you to confirm it instead of refusing, so upgrade an older CLI rather than editing the known-hosts file.

runtime_ssh_plan_required — the Application's organization is on a plan that does not include Runtime SSH.

runtime_ssh_provider_plan_required — the organization's plan includes the Liskov relay but not the provider the manifest names. Today that means a manifest with "kind": "tailscale" on Developer. A stored V4 manifest cannot be republished to change provider. For a new supported setup, review the V5 managed path and its plan requirements.

runtime_ssh_operator_key_withdrawn — this key's access was withdrawn for your organization. Ask an administrator of the organization to lift the withdrawal with withdrawn-key remove, or connect with another authorized key.

runtime_ssh_service_credit_required — the organization's shared log and Runtime SSH byte usage is above its included allowance, and its available Service Credit is zero or negative. Open Billing & funding and check currentPeriod.byteAllowance.runtimeSshServiceCreditRequired together with the available balance. Restore a positive Service Credit balance before retrying. The refusal stops only a new session; a managed SSH session that is already open continues until its normal end.

access_proxy_rejected_session_already_open — a session is already open on this job. Managed Runtime SSH allows one session per job at a time; retry when it closes.

access_proxy_rejected_connector_not_registered — the runtime has not connected to the relay for this job. Check that the job is still running. If its sandbox was relaunched moments ago, the access sidecar reconnects on its own, usually within a minute; allow up to three, then retry. If it does not, or its helper stopped, that is terminal for this run: launch a new job.

access_proxy_rejected_connector_unavailable — the runtime's connection to the relay is not ready yet. Retry in a few seconds.

access_proxy_rejected_credential_rejected — the relay refused the one-time ticket. This is not your key: the ticket is minted seconds before use, so a refusal here is a relay or control-plane fault. Retry once, then contact support with the attachmentId and the time.

Your session ends by itself — sessions last at most two hours, and a connection that stops answering the relay's heartbeat is closed after 60 seconds. Reconnecting is normal and safe; it issues a fresh one-time ticket.

A forward fails — if the CLI rejects -L, -D, or -N as an unknown flag, upgrade to proof-cli-liskov 0.17.0 or later. If the local port is already in use, choose another local port. If SSH refuses the forwarding channel, the job may still be running an older helper that disables local forwarding; it needs its next run on a helper that allows it. If the channel opens but the destination cannot be reached, check that the service is listening at the host and port you named, as reached from the job. A successful SSH connection alone does not verify the forwarded service.

What this does not do​

  • It does not change your job's lifecycle. Connecting, disconnecting, or losing SSH does not restart, replace, or extend it.
  • It does not give Liskov the ability to authenticate as you. Your private key never leaves your machine.
  • It is not inbound ingress. It does not publish a port or serve traffic to anyone; see the capability matrix for hosted ingress, which is not part of v1.

Next​