Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion docs.json
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,8 @@
"pages": [
"learn/architecture",
"learn/vault-proxy",
"learn/sandboxes"
"learn/sandboxes",
"learn/tty-auth"
]
},
{
Expand Down
1 change: 1 addition & 0 deletions installation.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ Recommended: AWS EKS for the sandbox cluster, Render for web + worker.
| `LITELLM_API_KEY` | LiteLLM gateway key. |
| `BASE_URL` | Public URL of the LAP deployment. |
| `ENCRYPTION_KEY` | Base64-encoded 32-byte key. Generate: `node -e "console.log(require('crypto').randomBytes(32).toString('base64'))"` |
| `HARNESS_AUTH_TOKEN` | Shared secret for sandbox TTY authentication. See [TTY auth](/learn/tty-auth). Generate: `openssl rand -hex 32` |

<Warning>
Never set `K8S_SKIP_TLS_VERIFY=true` against a production cluster — it disables TLS verification entirely.
Expand Down
49 changes: 49 additions & 0 deletions learn/tty-auth.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
---
title: "TTY Authentication"
description: "How HARNESS_AUTH_TOKEN secures terminal connections to sandbox pods."
---

Every sandbox pod exposes a `/tty` WebSocket endpoint that the `lap` CLI and browser terminal attach to. Access is gated by a shared secret — `HARNESS_AUTH_TOKEN`.

## Why it exists

AWS ALB (and most corporate proxies) strip `Authorization` headers from WebSocket upgrade requests. A header-only auth scheme would silently fail behind a load balancer. The harness accepts the token as a `?token=` query parameter instead, which survives the upgrade.

## How it flows

```
litellm-env secret
└─ HARNESS_AUTH_TOKEN=<value>
├─► web/worker pod env ──► toApiSession() returns tty_token in session API
│ │
│ └─► lap CLI appends ?token=<value> to WS URL
└─► sandbox pod env (injected at creation)
└─► harness verifies ?token= on every /tty connect
```

If `HARNESS_AUTH_TOKEN` is absent from the platform env, `tty_token` in the session response is `null` — the CLI connects with no token and the harness returns 401.

## Bootstrap

The deploy pipeline seeds `HARNESS_AUTH_TOKEN` automatically on first deploy. For existing clusters, set it once:

```bash
token=$(openssl rand -hex 32)
kubectl patch secret litellm-env -n default --type=json -p="[
{\"op\":\"add\",\"path\":\"/data/HARNESS_AUTH_TOKEN\",\"value\":\"$(printf '%s' "$token" | base64 | tr -d '\n')\"}
]"
kubectl rollout restart deployment/litellm-web deployment/litellm-worker
```

<Warning>
Existing warm-pool pods were created without the token and will still return 401. Delete them after the restart so new pods pick up the value.
</Warning>

## Rotating the token

1. Generate a new value: `openssl rand -hex 32`
2. Update the secret and restart web + worker (same commands as bootstrap above).
3. All **new** sessions pick up the new token immediately.
4. Any **active** session (already `ready`) holds the old token in its `tty_token` field — those sessions will stop accepting TTY connections after the rotation until restarted.