Metadata-Version: 2.5
Name: cryptolabs-sre-agent
Version: 0.3.0
Summary: CryptoLabs SRE reverse-tunnel agent — connects your server to CryptoLabs AI SRE
Project-URL: Homepage, https://cryptolabs.co.za
Project-URL: Discord, https://discord.gg/7yeHdf5BuC
Author-email: CryptoLabs <support@cryptolabs.co.za>
License-Expression: MIT
Requires-Python: >=3.10
Requires-Dist: aiohttp>=3.9
Requires-Dist: click>=8.1
Requires-Dist: websockets>=13.0
Provides-Extra: cli
Requires-Dist: keyring>=25; extra == 'cli'
Description-Content-Type: text/markdown

# CryptoLabs SRE Agent

A lightweight reverse-tunnel agent that connects your server to the
[CryptoLabs AI SRE](https://www.cryptolabs.co.za/ai-sre/) investigation
platform — **no SSH keys, no port forwarding, no firewall changes**.

## Quick Start

```bash
# Install (requires Python 3.10+)
pipx install cryptolabs-sre-agent

# Connect using the token from your CryptoLabs SRE dashboard
# Option 1: token file (recommended — not visible in process list)
echo "YOUR_TOKEN" > /etc/sre-agent.token
chmod 600 /etc/sre-agent.token
sudo $(which sre-agent) connect --token-file /etc/sre-agent.token

# Option 2: environment variable
export SRE_AGENT_TOKEN="YOUR_TOKEN"
sudo $(which sre-agent) connect

# Option 3: CLI flag (token visible in `ps aux` — use for quick testing only)
sudo $(which sre-agent) connect --token YOUR_TOKEN
```

## How it Works

1. **Install** — `pipx install cryptolabs-sre-agent`
2. **Run** — `sudo $(which sre-agent) connect --token-file /etc/sre-agent.token`
3. The agent opens an outbound WebSocket (`wss://`) to `sre.ai.cryptolabs.co.za`
4. The token links this agent to your registered server on the dashboard
5. Commands are sent by the SRE system, executed locally, results returned

## Security

### Network
- **TLS enforced** — agent refuses non-`wss://` connections in production
- **Outbound only** — no inbound ports opened on your machine
- **Heartbeat timeout** — server drops idle connections after 90s of silence

### Authentication
- **HMAC-SHA256 signed tokens** — cryptographically bound to a single server
- **Token hashed in storage** — only a SHA-256 hash is stored server-side (not the raw token)
- **Rate limiting** — 10 failed auth attempts per IP triggers a 5-minute lockout
- **10-second auth deadline** — connections that don't authenticate are dropped

### Execution Safety
- **Server-side allowlist** — commands are validated by the SRE API's command
  allowlist *before* reaching the agent
- **Agent-side blocklist** — defense-in-depth: the agent independently blocks
  catastrophic commands (`rm -rf /`, `mkfs`, `dd` to disk, fork bombs,
  `halt`/`poweroff`, writes to `/etc/shadow`, `/root/.ssh/authorized_keys`)
- **Output truncation** — stdout/stderr capped at 1 MB per field
- **Concurrent limit** — max 10 commands in parallel to prevent resource exhaustion
- **Command timeout** — every command has a configurable timeout (default 300s)

### Token Handling
- **`--token-file` recommended** — avoids exposing token in process list
- **Environment variable** (`SRE_AGENT_TOKEN`) — alternative to file
- **CLI flag** (`--token`) — agent warns when this is used

### Revocation
- Deleting the server from the dashboard instantly disconnects the agent
- Token revocation via API (`DELETE /agent/token/{server_id}`)
- Agent auto-shuts down when server sends disconnect message

## Remote terminal (local consent)

The agent can open interactive root shells and run commands for the machine owner
through CryptoLabs (`srecli`). **It is off by default and only you can turn it on, on
the machine itself.**

```bash
sudo sre-agent terminal on      # allow (prints exactly what this allows)
sudo sre-agent terminal off     # refuse
sre-agent terminal status       # prints on/off and, when off, why
```

- Consent is the file `/etc/sre-agent.conf` (`[terminal]` / `enabled = true`). It counts
  only if it is a regular file owned by root and not writable by group or others;
  anything else (missing, wrong owner, unreadable, `enabled` not `true`) means off.
  The agent re-reads it for every request and every 5 seconds during a session; turning
  it off ends open sessions.
- Each session uses its own outbound `wss://` connection; the agent never takes a
  host or URL from the server. Sessions end when the connection drops, on timeout
  (PTY: 30 min idle, 8 h maximum), or when you turn consent off. At most 3 run at once.
- Read-only commands run inside a `systemd-run` sandbox (no writes outside a private
  `/tmp`, no signals to other processes, no docker, no kernel/mount/clock calls).
  Read-only is not confidential: output can contain secrets.
- This switch protects against mistakes and policy, not against a compromised CryptoLabs
  server, which can already run root commands through the agent. The hard stop is
  `sudo systemctl stop sre-agent`.

Protocol: `docs/contracts/2026-10-01-terminal-agent-protocol.html`.

## srecli: the owner's command-line client

`srecli` runs on **your own computer** (not on the servers) and reaches the machines
you registered with CryptoLabs AI SRE. It never needs root and is installed from the
same package as the agent:

```bash
pipx install 'cryptolabs-sre-agent[cli]'   # [cli] adds keyring, for the macOS Keychain
srecli login                                # approve the code in your browser
srecli servers                              # hostname, online, terminal setting, agent version, ready
srecli show venter-h100x4-01                # details of one server (a hostname or a server id)
srecli exec venter-h100x4-01 -- nvidia-smi -L
srecli elevate venter-h100x4-01             # ask for write access; approve it in the browser
srecli exec venter-h100x4-01 -w --timeout 120 -- systemctl restart nginx
srecli connect venter-h100x4-01             # live root shell (needs the same approval); Ctrl-] leaves
srecli logout                               # revokes the key in use (SRECLI_KEY first, else the stored key)
```

- `exec SERVER [-w] [--timeout N] -- COMMAND...`: the words after `--` are joined with single
  spaces and run as one shell command. Output goes to your stdout and stderr, piped stdin is
  streamed, and the exit status is the remote one (`124` timeout, `126` refused, `143` when a
  grant ended, `255` connection lost, `2` usage error or an ambiguous hostname). Leaving on
  purpose decides the status: Ctrl-] in `connect` is `0`, Ctrl-C is `130`, SIGTERM is `143`,
  whatever the server sends afterwards. `1` is a local error, including a rejected key
  (`Your key is not valid. Run: srecli login`), `Rate limited, try again later`, and a server
  whose remote terminal is not enabled.
- Without `-w` a command must be on the read-only command list. Write commands and `connect`
  need an approved grant for the key you are using, which `srecli elevate` requests. The grant
  belongs to that key only and is shown to you in the browser with a second-factor check.
- `--json` on `servers` and `show` prints the API answer as JSON.
- Browser: `srecli` opens only `https://www.cryptolabs.co.za/` links (and loopback links when
  `SRECLI_URL` is a loopback test server). Any other link, or a machine with no graphical
  browser, only gets the link printed.
- Text from the server (hostnames, OS info, labels, messages) has escape sequences, control
  characters and every invisible or unassigned Unicode character (categories Cf, Co, Cs, Cn: direction
  marks, zero-width and tag characters) removed, and line breaks turned into spaces, before it is
  printed. A missing value prints as `-`. `--json` keeps the raw values, escaped by JSON.
- An error answer that is not JSON (for example from nginx) is reported as the HTTP status and a
  short generic text. A refused session upgrade maps like the REST answer: 401 is the key hint,
  429 is rate limited, 503 is "not enabled".
- `srecli elevate` waits for your approval and stops with exit `1` ("the request expired") 30 s after
  the request's `request_expires_at` (at most an hour from the request in any case). It retries a 429
  or 503 until then; a grant status it does not know is an error.
- `srecli` never sends a key that is not `sre_pk_` plus 43 letters and digits, and ignores
  proxy environment variables (`HTTPS_PROXY` and the like) for both REST and the session.
- `srecli login` checks that the key can be stored before it asks for a code. If storing fails
  after a key was issued, it revokes that key at once and exits `1`. With a keyring it tries a
  throwaway entry first and falls back to the file if the keyring refuses. If the key is stored but
  the older copy in the other store cannot be removed, that is a warning, not a failed login.
- The key is stored in the macOS Keychain (service `srecli`) when `keyring` is installed, else
  in `~/.config/srecli/credentials` (mode 0600, with a warning). It is never put on a command
  line or printed. `SRECLI_KEY` in the environment takes precedence over a stored key.
  `srecli` warns when the key expires within 7 days; run `srecli login` again to renew.
- `srecli logout` revokes the key in use: `SRECLI_KEY` first, then the stored key. It deletes the
  stored copy only if it is that key, so logging out an assistant's `SRECLI_KEY` never touches the key
  you stored for yourself. If the revoke fails, nothing is deleted and you can retry.

### Using srecli with an AI coding assistant

- Give the assistant its own key: run `srecli login` on a separate account or computer, or set
  `SRECLI_KEY` for the assistant's process. A read key can list servers and run read-only
  commands and nothing else.
- Writes need **your** approval: the assistant can run `srecli elevate`, but the grant only
  starts after you approve it in your browser, it lasts at most 60 minutes (default 30), and it applies to the
  assistant's key only, never to your own key.
- Read output can contain secrets (environment variables, config files, logs). Anything
  `srecli exec` prints is visible to the assistant. Run `srecli logout` with the
  assistant's `SRECLI_KEY` set when you are done: that revokes the assistant's key and leaves yours.

## Environment Variables

| Variable | Description |
|----------|-------------|
| `SRE_AGENT_TOKEN` | Alternative to `--token` flag |
| `SRE_AGENT_TOKEN_FILE` | Alternative to `--token-file` flag |
| `SRE_RELAY_URL` | Override the relay URL (default: `wss://sre.ai.cryptolabs.co.za/agent/ws`) |

## Running as a Service

```bash
# systemd (Linux) — using token file for security
sudo tee /etc/systemd/system/sre-agent.service << 'EOF'
[Unit]
Description=CryptoLabs SRE Agent
After=network-online.target
Wants=network-online.target

[Service]
ExecStart=/root/.local/bin/sre-agent connect --token-file /etc/sre-agent.token
Restart=always
RestartSec=10

[Install]
WantedBy=multi-user.target
EOF

sudo systemctl enable --now sre-agent
```

## Support

Need help? Join our Discord: https://discord.gg/7yeHdf5BuC

## Development

```bash
cd services/sre-agent
pip install -e ".[dev]"
pytest
```
