---
id: browser-console
title: Browser console
sidebar_label: Browser console
---

An SSH, RDP or VNC session in a browser tab, with no client software and no
inbound port on the target beyond the one the protocol already uses.

Protocol handling is Apache Guacamole's the control plane proxies a WebSocket
to a **guacd** daemon and manages credentials around it.

## Requirements

- `guacd` running and reachable from the server. The bundled Compose stack
  includes it; `GUACD_ADDR` defaults to `guacd:4822`.
- `KEY_ENCRYPTION_KEY` set, since every credential involved is stored encrypted.
- The target's **agent must be online**. Console traffic is relayed over the
  agent's existing outbound connection, so the control plane never needs a route
  to the server's address — but it does need the agent.
- No inbound port on the target, beyond what the protocol already listens on
  locally. A service bound only to `127.0.0.1` works, because the agent dials
  loopback on the target itself.

## Opening a session

From a server's page, choose **Console**. Then:

1. The UI calls `POST /api/console/connect`, which mints a **one-time** session
   token. If the target's agent is not connected, this fails immediately with
   `409 agent_offline` rather than hanging.
2. The browser opens a WebSocket to `GET /api/console/tunnel` with that token.
3. The server marks the token consumed atomically, so a second use cannot
   race and proxies the connection to guacd.

## Credentials

### SSH

Authenticates with a private key stored in the [key library](./ssh-keys.md). The
key must have its private half uploaded; a public-only key cannot open a
session.

### RDP and VNC

You supply credentials when connecting. They are encrypted, **single-use**, and
consumed when the tunnel opens. They are not retained for the next session.

:::info Why single-use
A stored console credential is a standing grant to that machine for anyone who
can reach the endpoint. Consuming it at tunnel-open means a leaked session token
is worth one connection at most, and only until it is used.
:::

## Session behaviour

Closing the tab ends the session. There is no reconnect and no session
persistence reopening mints a new token and a new connection.

## Auditing

Opening a console is an audited action, with actor, server and time. What
happens _inside_ the session is not recorded: there is no session capture or
keystroke log. If you need that, it has to come from the target machine.

## When it does not work

| Symptom                          | Cause                                                                           |
| -------------------------------- | ------------------------------------------------------------------------------- |
| Connects then closes immediately | guacd unreachable check `GUACD_ADDR` and that the container is up               |
| SSH refuses the key              | The stored key has no private half, or is not in the target's `authorized_keys` |
| RDP fails on a fresh credential  | Credentials are consumed on open; a retry needs them entered again              |
| Hangs, then disconnects           | The agent never claimed the relay, nothing is listening on the protocol port on the target's own loopback address, or guacd never dialled in time. Check the audit log for `console.proxy_failed` — its reason (`agent_timeout`, `dial_refused`, `guacd_timeout`, `rejected`) names which |
