# How it works

URL: https://shellz.dev/docs/how-it-works

> The path a session takes from a login on your server to a replay in your browser, and the pieces in between.

shellz has three parts: an agent on each server or in each container, a cloud service that stores and relays encrypted recordings, and the dashboard in your browser, which is the only place recordings are decrypted.

```text
  your server                        shellz                         your browser
 ┌──────────────────────┐          ┌───────────────────────┐       ┌──────────────────────┐
 │ login → session shell│          │ ingest: checks        │       │ re-auth, unwrap the  │
 │       → recorder     │  signed, │ signature + chain     │ live  │ session key, verify, │
 │ encrypt + sign each  │─────────▶│ write-once storage    │──────▶│ decrypt, play        │
 │ segment, then upload │ encrypted│ (365-day lock)        │       │                      │
 └──────────────────────┘          └───────────────────────┘       └──────────────────────┘
```

## 1. Every login goes through the recorder

On a Linux server, the installer changes the login shell of root and of every regular account (UID from `UID_MIN` in `/etc/login.defs` up to 59999) to the shellz session shell, `/usr/local/libexec/shellz-shell`. The original shells are saved and restored if shellz is removed. Service accounts with `nologin` or `false` keep their shells.

When someone logs in by SSH, at a console, with `su -` or by any other route that starts a login shell, the session shell asks the recorder (the `shellz-recorder` systemd service) to take over. The recorder:

1. Checks who is asking. The request travels over a root-only Unix socket, and the recorder reads the caller's identity from the kernel, not from anything the caller says.
2. Opens a new pseudo-terminal, starts the user's real shell inside it, and copies everything between the user and that shell.
3. Records the output and what was typed, plus terminal resizes. Input typed while the terminal has echo turned off, such as password prompts, is left out.

A session auditor watches for logins that avoided the session shell. If it finds one, it ends that session and raises an `unrecorded-session-terminated` alert. Under the fail-open policy it leaves the session running and raises `unrecorded-session` instead.

On a Docker host, the recorder also sits in front of the Docker socket so that `docker exec` and similar commands are recorded. Inside containers, a container entrypoint does the same job without systemd. See [Docker host](https://shellz.dev/docs/install/docker-host.md) and [Container image](https://shellz.dev/docs/install/container-image.md).

## 2. Recordings are encrypted on the server

Each session gets a new random 32-byte data key. The recorder:

- writes the session in two forms, a terminal replay (asciinema-compatible `.cast`) and a plain-text transcript;
- cuts them into segments of at most 1 MiB, compresses each with gzip and encrypts it with AES-256-GCM under the session's data key;
- wraps the data key to your org's X25519 public key, so only the org's private key can open it;
- signs each segment header with the server's own Ed25519 key and links it to the previous segment with a SHA-256 hash chain.

The server never holds a key that can decrypt a finished recording. Its own key can only sign. See [Encryption](https://shellz.dev/docs/security/encryption.md) for the formats and algorithms.

## 3. Segments stream up about once a second

The recorder flushes a segment every second, and every 250 ms while someone is watching live. Segments go first to a spool on disk (`/var/lib/shellz/spool`), then to `ingest.shellz.dev`. If the network is down, segments wait in the spool until it comes back. The [recording policy](https://shellz.dev/docs/guides/recording-policy.md) decides what happens if the spool fills up.

Every 30 seconds the agent sends a signed heartbeat to `api.shellz.dev`. The heartbeat reports the recorder's health, active sessions and any local alerts, and returns the current policy and org keys. A server that misses heartbeats for 90 seconds is marked offline and raises a `heartbeat-missing` alert.

The agent talks only to three hosts, all over HTTPS: `api.shellz.dev`, `ingest.shellz.dev` and `releases.shellz.dev`.

## 4. shellz checks and stores ciphertext

When a segment arrives, shellz checks that the server's signature is valid, that the segment's sequence number and hash link follow the previous segment, and that the server belongs to your org. It then writes the segment once to object storage. Stored segments can't be overwritten or deleted for 365 days, by anyone, including shellz.

shellz can read the segment header: which server, which user, when, from which address, the terminal size and, in standard mode, the command line. It can't read what was typed or shown.

Live viewers receive the same ciphertext as it arrives.

## 5. Your browser decrypts

When you open a recording:

1. shellz asks you to confirm it's you if your last sign-in was more than 10 minutes ago, and writes the view to the audit log.
2. Your browser gets the session's wrapped data key unwrapped:
   - In **standard mode**, shellz unwraps it with the org private key it holds in a sealed form and sends the data key to your browser over TLS.
   - In **Perfect Secrecy** mode, shellz doesn't have the org private key. Your browser unlocks it with your passkey and unwraps the data key itself. See [Perfect Secrecy](https://shellz.dev/docs/security/perfect-secrecy.md).
3. Your browser checks every signature and hash link, decrypts and decompresses the segments, and plays them. The integrity badge shows **verified**, **gap** or **tampered**.

You can also decrypt downloaded segments offline with the `shellz decrypt` command, without the dashboard. See the [CLI reference](https://shellz.dev/docs/reference/cli.md#shellz-decrypt).

## 6. The agent protects itself

A user with root access could try to stop recording. shellz makes that loud rather than quiet:

- The recorder unit refuses manual stops and restarts automatically.
- Every 15 seconds, and immediately when `/etc/passwd` changes, the agent checks its files, the login shells and the SSH settings. Anything changed is put back and reported as a `tamper` alert.
- Removing shellz requires approval from an owner or admin in the dashboard. Otherwise `shellz uninstall` is refused and reported.
- If the agent stops sending heartbeats, the dashboard raises an alert after 90 seconds.

See [Tamper resistance](https://shellz.dev/docs/security/tamper-resistance.md) for the full list and its limits.

## 7. Updates are signed

The agent checks for a new release about every 6 hours. Releases are signed with the shellz release key, which is pinned in the agent and the installer, and the agent refuses older versions. New versions run a self-test before switching, and the agent rolls back automatically if the new version fails to start. See [Updates and removal](https://shellz.dev/docs/guides/updates-and-removal.md).
