System Requirements
Controller
Section titled “Controller”The controller is a single Go binary. Everything below is driven by concurrent sessions — not by how many people are on the team, and not by how many targets are registered.
OS / architecture: Debian 12 or Ubuntu 22.04+ on amd64. Container images are amd64. Only amd64 artefacts are published.
The main workload is double encryption: TLS with the client and SSH with the target. Modern CPUs with AES-NI handle this cheaply — for interactive terminal work, CPU is almost never the constraint.
| Concurrent sessions | vCPU |
|---|---|
| up to 50 | 1 |
| 50–200 | 2 |
| 200+ | 4 |
These are for interactive sessions. Sustained file transfer and RDP are different workloads — see Network and RDP.
Memory
Section titled “Memory”Per session the controller holds roughly 256 KB of copy buffers plus its goroutine stacks (6–10 goroutines at ~8 KB), so about 320 KB per session. The Go process baseline is ~30 MB.
Go does not hand memory back to the OS immediately, and at the default GOGC=100 the resident
size settles at roughly twice the live heap — so provision about double the arithmetic, then
leave room for a spike.
| Concurrent sessions | Expected resident | Provision |
|---|---|---|
| up to 50 | ~90 MB | 512 MB |
| 50–200 | ~190 MB | 1 GB |
| 200+ | ~380 MB | 2 GB |
The gap between the two columns is deliberate headroom, not a hidden cost: a controller serving
200 concurrent sessions genuinely uses a few hundred megabytes. The Helm chart ships
requests: 256Mi and limits: 1Gi, which matches the middle row.
Network
Section titled “Network”All session traffic passes through the controller twice — client → controller → target and back — so a transfer of N bytes costs 2N on the controller’s interface. That interface, not the CPU, is what saturates first.
Interactive terminal traffic is negligible: keystrokes and screen output are a few KB/s per session, so even 200 concurrent shells is single-digit Mbit/s. Size the link for the two things that are not interactive — file transfer and RDP.
| Workload | Link |
|---|---|
| Interactive sessions only (SSH · Kubernetes · SQL) | 100 Mbit/s |
Interactive + occasional scp/rsync | 1 Gbit/s |
| Heavy transfer, or 50+ concurrent RDP | 10 Gbit/s |
Recommendation: put the controller in the same datacentre as the targets. The double hop costs latency as well as bandwidth, and latency is what an interactive user feels.
RDP is a different workload
Section titled “RDP is a different workload”RDP is brokered as a byte tunnel: the desktop client opens a WebSocket to the controller, the controller dials the target on 3389, and traffic is pumped in both directions. It works exactly like the other protocols from an access-control point of view — and nothing like them from a capacity point of view.
A single RDP session typically runs 0.5–5 Mbit/s, and more with video, animation or a large high-colour desktop. That is two to three orders of magnitude above an interactive shell, and it is sustained rather than bursty. Twenty concurrent RDP sessions can mean 100–200 Mbit/s through the controller once the double hop is counted.
If RDP is a significant part of your usage, size the link from the RDP sessions alone and treat everything else as rounding.
Only terminal sessions are recorded, as asciinema .cast files.
Estimate 1–5 MB per session-hour, driven by how much output the session produces — a shell
someone is typing in sits at the bottom of that range, while tail -f on a busy log or a
repeated full-screen redraw (top, htop) will exceed it.
RDP sessions are not recorded. The tunnel carries the RDP protocol rather than a terminal
stream, so there is no .cast to write; RDP produces audit events only. Plan controls for
Windows targets accordingly.
PostgreSQL
Section titled “PostgreSQL”Version 16+. Any small managed instance is enough: the database handles session CRUD and audit writes, which is not a high-throughput workload. 1 vCPU / 512 MB covers most deployments.
Size the disk separately, because the audit log is the one thing here that grows without bound. Growth is driven by events rather than by session length — a session start and close, plus a row per command on SSH — so a few hundred kilobytes per session-hour is a reasonable first estimate, and your own first month is a better one.
Two things raise it sharply:
- Database query audit (Ultimate) records every SQL statement that crosses a brokered
connection. A human at a
psqlprompt is cheap; a session driving a script is not. - The default is to keep everything.
TESSERA_AUDIT_RETENTION_DAYSis0— no purging — so without setting it the answer to “how big does this get” is “as big as the controller’s whole history”. Set it to your compliance window and size the disk for that window.
Limitations
Section titled “Limitations”Tessera is optimised for interactive access, not bulk file transfer. Every byte travels client → controller → target → controller → client.
Benchmark, 20 concurrent SSH sessions over loopback:
| Native SSH | Tessera proxy | |
|---|---|---|
| Aggregate throughput | ~540 MB/s | ~440 MB/s |
| Overhead | — | ~20% |
Loopback is deliberate: it isolates the cost of the proxy itself — the extra encrypt/decrypt pass
and the copies — from the network. On a real network the double hop adds its own bandwidth and
latency cost on top, so treat ~20% as the floor rather than the expected figure. Reproduce it
against your own hardware with the loadtest tool’s -compare mode.
For high-throughput work — CI/CD artefacts, database dumps, backups — route the transfer outside Tessera and use Tessera for interactive access and auditing.
We would like to count visits with Google Analytics, which needs a cookie. Nothing is loaded and nothing is stored unless you accept. What this sets.