# The connection

> The conn object passed to every handler, covering id, terminal size, write, close, and key encoding

Every handler receives a `conn`: a single connected terminal session. It is how you read the terminal's size, write to the screen, and end the session. All of your app's users are multiplexed into one app instance, so each user gets their own `conn`.

```ts
interface Conn {
  readonly id: string;
  readonly cols: number;
  readonly rows: number;
  write(data: string): void;
  close(): void;
}
```

## conn.id

```ts
readonly id: string
```

A unique identifier for this session. Use it as the key for per-connection state in a `Map`.

```js
sessions.set(conn.id, { score: 0 });
```

## conn.cols and conn.rows

```ts
readonly cols: number  // terminal width  (default 80)
readonly rows: number  // terminal height (default 24)
```

The current terminal size in character cells. If the size is unknown they default to 80 by 24. They are updated automatically **before** [`onResize`](/sdk/lifecycle) fires, so reading them in any handler always gives the live size.

```js
const rule = '-'.repeat(conn.cols);
```

## conn.write(data)

```ts
write(data: string): void
```

Writes a string straight to the terminal. There is no buffering and no implicit newline. Non-string values are coerced with `String()`.

```js
conn.write('\x1b[2J\x1b[H');               // clear screen, cursor home
conn.write('\x1b[1;32mready\x1b[0m\r\n');  // bold green "ready", then a newline
conn.write('size: ' + conn.cols + 'x' + conn.rows + '\r\n');
```

### Use \r\n, not \n

`write` sends bytes verbatim, so you control every character including line endings. Use `"\r\n"` to start a new line, not `"\n"`. A bare `\n` moves the cursor **down** but does not return it to column 0, so successive lines stair-step to the right. The `\r` (carriage return) is what returns the cursor to the start of the line.

<Callout type="warn">
Forgetting the `\r` is the most common terminal-output bug. If your output drifts diagonally across the screen, you are missing carriage returns.
</Callout>

## conn.close()

```ts
close(): void
```

Ends the SSH session. Your [`onClose`](/sdk/lifecycle) handler still fires afterwards, so closing is a good place to request teardown and let `onClose` do the cleanup.

```js
.onKey((conn, data) => {
  if (data === 'q' || data === '\x03') return conn.close();
})
```

## Key encoding

The `data` string passed to [`onKey`](/sdk/lifecycle) is the decoded input. It is usually a single character, but special keys arrive as multi-character escape sequences and control keys as control codes.

| Input | `data` |
| --- | --- |
| An ordinary key | the character, for example `"q"` |
| Enter | `"\r"` |
| Ctrl-C | `"\x03"` |
| Ctrl-D | `"\x04"` |
| Up arrow | `"\x1b[A"` |

Escape sequences arrive whole, so you can match them with a single comparison. For richer decoding (named keys, modifiers), use the [UI layer's `useInput`](/sdk/ui/hooks).

## Terminal output notes

The sandbox is pure JavaScript with no DOM and no global `fetch`. A few things to keep in mind when writing to the terminal:

- **Include ANSI escapes yourself** for color and cursor control. For example, `"\x1b[2J\x1b[H"` clears the screen and homes the cursor.
- **`console.log` goes to the server logs, not the user's terminal.** Use `conn.write` for anything the user should see, and `console.log` for your own debugging.
- **There is no built-in network.** Use the SDK [`fetch`](/sdk/fetch) for HTTP requests; the global `fetch` does not exist in the sandbox.

If you would rather build with components than hand-write escape sequences, use the React [UI layer](/sdk/ui).

### A handy ANSI cheat sheet

| Sequence | Effect |
| --- | --- |
| `\x1b[2J` | Clear the entire screen |
| `\x1b[H` | Move cursor to top-left (home) |
| `\x1b[<row>;<col>H` | Move cursor to a position (1-based) |
| `\x1b[K` | Clear from the cursor to end of line |
| `\x1b[?25l` / `\x1b[?25h` | Hide / show the cursor |
| `\x1b[1m` ... `\x1b[0m` | Bold ... reset all attributes |
| `\x1b[3<n>m` | Foreground color (`30` to `37`) |
| `\x1b[38;2;<r>;<g>;<b>m` | 24-bit truecolor foreground |

---

Source: https://docs.voxal.sh/sdk/connection
