---
name: runin
description: Use when a user wants to create or manage runin.eu Machines, run coding tasks over SSH, transfer project files, or share an app preview.
---

# Code on runin.eu

runin.eu provides persistent Linux Machines. Use the local SSH client; no runin CLI or API token is needed.

- `ssh runin.eu <command>` manages Machines. This endpoint accepts runin commands, not shell commands.
- `ssh <machine-id>@runin.eu '<shell command>'` runs inside a Machine. The Machine ID must be the SSH username; `ssh <machine-id>.runin.eu` alone does not select it.

## Connect and choose a Machine

The user's SSH public key must already be registered with their Tenant. Check access and list Machines:

```sh
ssh -T -o BatchMode=yes runin.eu whoami
ssh -T -o BatchMode=yes runin.eu ls --json
ssh -T -o BatchMode=yes runin.eu help
```

Use the user's SSH configuration or add `-i /path/to/key -o IdentitiesOnly=yes` when they specify a key. `Permission denied (publickey)` means the offered key was not accepted. A person who holds an invite connects as `ssh join@runin.eu CODE` instead (see Members below). Have the user register their first public key through their account administrator or authenticated runin CLI. Keep private keys local. Resolve host-key verification failures before retrying; retain SSH host-key checking.

Reuse the Machine selected for the task. When the task calls for a new one:

```sh
ssh -T -o BatchMode=yes runin.eu new --size small --name coding --json
```

`new` waits for the Machine to run. Save its returned `id`; `--name` is metadata, not an SSH address. If creation times out or the connection drops, check `ls --json` before creating another Machine. Use `ssh runin.eu new --help` for other sizes and options.

The default `base` Template has a 10 GiB disk. Use `--template base-30gib` for
30 GiB; `--size` selects CPU and memory separately. The text output of
`ssh runin.eu ls` includes a `NAME` column. `ssh runin.eu help new` also lists
the creation options.

To give Machines your own software preinstalled, build a Template from a
Dockerfile. Any Linux image works (`FROM alpine`, `python:3.12-slim`, your own
app image); you log in as `runin`. An image without systemd runs its
`ENTRYPOINT`/`CMD` as the workload, and the Machine stays up if it exits. For
systemd and the usual tools, start from the base Dockerfile:

```sh
curl -fsSL https://runin.eu/templates/base.Dockerfile > Dockerfile
echo 'RUN apt-get update && apt-get install -y --no-install-recommends ripgrep' >> Dockerfile
ssh -T runin.eu template build --name my-app --file - < Dockerfile
ssh runin.eu new --template my-app --json
```

Only the Dockerfile is sent, with no build context. One build runs at a time.

## Work remotely

Replace `MACHINE_ID` with the returned ID and adapt the project path and commands:

```sh
ssh -T -o BatchMode=yes MACHINE_ID@runin.eu 'mkdir -p ~/app'
scp -r ./project/. MACHINE_ID@runin.eu:app/
ssh -T -o BatchMode=yes MACHINE_ID@runin.eu 'cd ~/app && git status --short'
```

Transfer only the project files needed for the task. Run its setup, edits, builds, and tests inside the Machine. Each SSH command starts a new shell, so include `cd` and any required environment setup each time. Quote remote commands so local shell expansion does not change them. For multiline work, use a quoted heredoc:

```sh
ssh -T -o BatchMode=yes MACHINE_ID@runin.eu 'bash -se' <<'REMOTE'
cd ~/app
pwd
# Add the project's setup, build, or test commands here.
REMOTE
```

Use `-t` only for interactive programs. Keep long-running apps in a service or detached session with a log, then check that they started. Files persist between SSH sessions. Retrieve an artifact with `scp MACHINE_ID@runin.eu:app/path/to/artifact ./`.

## Preview and lifecycle

For an HTTP app, listen on `0.0.0.0` at the chosen port. Its address is `https://MACHINE_ID-PORT.runin.eu`; `https://MACHINE_ID.runin.eu` uses its default port. HTTPS access is private by default. When the user requests a shareable preview:

```sh
ssh runin.eu share MACHINE_ID --port 3000 --ttl 1h
```

Return the full generated URL, including its query string. Anyone holding that link can access the chosen port until expiry or revocation.

| Task | Command |
| --- | --- |
| List preview links | `ssh runin.eu share ls MACHINE_ID` |
| Revoke a link | `ssh runin.eu share revoke SHARE_ID` |
| Snapshot the current state | `ssh runin.eu snapshot MACHINE_ID --json` — the Machine pauses briefly and resumes at once; the answer names the new Snapshot and the frozen time |
| Fork from that moment | `ssh runin.eu fork MACHINE_ID -n 1 --snapshot SNAPSHOT_ID` |
| Pause or resume | `ssh runin.eu pause MACHINE_ID` / `ssh runin.eu resume MACHINE_ID` |
| Permanently destroy | `ssh runin.eu rm MACHINE_ID` |

Keep the Machine available after coding unless cleanup was requested. Destroy only Machines covered by the user's deletion request. Finish by reporting the Machine ID, work location, test results, and any requested preview URL.

## Members and sharing with people

A Tenant has Members; every SSH key acts as one of them, and a Machine belongs to the Member who created it. `ssh runin.eu ls` shows a user's own and the Machines shared with them. To let another Member see and enter a Machine:

```sh
ssh runin.eu member ls                      # who is in; pending invites show for a Tenant admin
ssh runin.eu member invite anna             # prints the one-time command to send:
                                            #   ssh join@runin.eu ONE-TIME-CODE
ssh runin.eu share add MACHINE_ID anna      # anna sees it in ls and may enter it
ssh runin.eu share ls MACHINE_ID            # links and Machine shares together
ssh runin.eu share rm MACHINE_ID anna       # take it back
ssh runin.eu transfer MACHINE_ID anna       # hand the Machine over (Tenant admin)
```

Anna runs the printed command with her own key and nothing else; it works once. `member role NAME admin|member` changes roles, and `member rm NAME` removes a Member (`--force` when a token of theirs was used recently). These are Tenant-admin operations; say so instead of retrying when the API refuses.

Tenant-internal networking crosses Member boundaries when enabled: Machines of the Tenant reach each other even when their owners cannot see each other. Privacy between Members is access control at the API and the Front Doors, not network isolation.


Runin cache Volume and repository-policy management are retired. Do not propose
`volume`, `cache policy`, or `new --volume` through CLI or SSH. ForgeSmith cache
policy uses operator-only `fsm-gh cache-policy` commands; customer credentials
cannot manage that authority.
