> ## Documentation Index
> Fetch the complete documentation index at: https://docs.echos.sh/llms.txt
> Use this file to discover all available pages before exploring further.

# Security

> Security model, guarantees, auth, SSRF prevention, and rate limiting.

# Security

EchOS is designed with a security-first mindset. It runs locally or on your own server, only calls the APIs you configure, and enforces strict input validation and content sanitization throughout. This page documents the security guarantees, authentication model, and defensive measures built into the system.

## What EchOS Does NOT Do

These are hard guarantees about what EchOS will never do to your system:

| Guarantee                           | Detail                                                                                                                                                                   |
| ----------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| Does not touch your file system     | EchOS only reads and writes inside its own `data/` directory. It never traverses your home directory, system files, or anything outside that scope.                      |
| Does not execute shell commands     | No user input, AI output, or plugin code ever reaches a shell. `exec`, `spawn`, and similar calls are absent from the codebase.                                          |
| Does not run code from AI responses | Claude's output is treated as text. EchOS never passes AI-generated strings to `eval()`, `Function()`, `vm.runInNewContext()`, or any other dynamic execution primitive. |
| Does not exfiltrate your data       | The only outbound calls are to the APIs you configure (Anthropic, OpenAI). No data is ever sent to any other third party.                                                |
| Does not store secrets in logs      | Pino redaction paths cover all common secret field names. API keys and tokens never appear in log output, error messages, or audit trails.                               |

***

## Authentication

| Interface          | Mechanism                                                                                     |
| ------------------ | --------------------------------------------------------------------------------------------- |
| Telegram           | User ID whitelist (`ALLOWED_USER_IDS`) enforced in middleware before every message            |
| Web API            | Bearer token (`WEB_API_KEY`) on every request + `userId` validated against `ALLOWED_USER_IDS` |
| CLI (`pnpm echos`) | Local only — no network, no auth                                                              |

The Web API also binds to `127.0.0.1` and enforces localhost-only CORS, so it is not reachable from the network even if the firewall is misconfigured.

## SSRF Prevention

All URL fetching goes through `validateUrl()` which:

* Only allows `http:` and `https:` protocols
* Blocks private IP ranges (10.x, 172.16-31.x, 192.168.x, 127.x)
* Blocks link-local (`169.254.x.x`) — covers AWS/Azure/GCP instance metadata service (IMDS)
* Blocks Azure platform endpoint (`168.63.129.16`)
* Blocks CGNAT range (`100.64.0.0/10`, RFC 6598)
* Blocks IPv6 ULA, link-local, loopback, and unspecified addresses
* Blocks `localhost`, `instance-data`, and cloud metadata hostnames
* Applied before any fetch operation

## Content Sanitization

* HTML content sanitized with DOMPurify before processing
* `sanitizeHtml()` strips all tags and re-escapes entities
* `escapeXml()` for XML context escaping
* AI output treated as untrusted data

## Content Size Limits

All external text content is validated against a maximum size before reaching
the AI or storage layer, preventing oversized-payload attacks:

```typescript theme={null}
import { validateContentSize, validateBufferSize } from '@echos/shared';

// Throws ValidationError if content exceeds 500 000 characters
validateContentSize(text, { label: 'article body' });

// Throws ValidationError if buffer exceeds 10 MiB
validateBufferSize(audioBuffer, { label: 'voice message' });
```

Default limits (overridable per call):

* Text: 500 000 characters
* Binary: 10 MiB

## Rate Limiting

* Token bucket algorithm (20 tokens, 1 token/second refill)
* Per-user tracking
* **Bounded to 10 000 distinct keys** — evicts the oldest entry when at capacity, preventing memory exhaustion from many unique callers
* Applied at middleware level in all interfaces

## Timing-safe Token Comparison

The Web API bearer-token check uses `timingSafeStringEqual()` (backed by
`crypto.timingSafeEqual` after SHA-256 hashing) instead of `===`. This
prevents timing side-channel attacks where an attacker could measure response
latency to guess the token character-by-character.

## Secret Management

* Pino configured with an expanded set of redaction paths covering common secret field names: `apiKey`, `token`, `password`, `secret`, `authorization`, `cookie`, `key`, `credential`, `credentials`, `privateKey`, `clientSecret`, `accessToken`, `refreshToken`, `bearer`, and nested variants
* HTTP headers `Authorization` and `Cookie` are also redacted
* Secrets never included in error messages
* `.env` files gitignored

## Audit Logging

* Separate audit logger instance
* Structured events with timestamps and user IDs
* Security-relevant events logged: auth failures, content access, mutations

## Input Validation

* All configuration validated with Zod schemas
* Tool parameters validated with TypeBox + AJV (via pi-agent-core)
* User input sanitized before storage
