diff --git a/README.md b/README.md index 73c3c73..ec8ec39 100644 --- a/README.md +++ b/README.md @@ -41,6 +41,68 @@ make lint # Run linter make build-prod # Production build (static, CGO disabled) ``` +## Session & Pod Lifecycle + +Each investigation gets its own sandbox pod. The MCP server is stateless — session +state lives in Kubernetes (pod labels/annotations) and is cached in memory for performance. + +### Request Flow + +``` +TARSy (LLM) + │ MCP tool call + X-Session-ID header + ▼ +MCP Server (stateless, multi-replica) + │ HTTP POST /exec + Bearer HMAC token + ▼ +Sandbox Pod (per-session, persistent bash) + │ stdout / stderr / exit_code + ▼ +MCP Server → TARSy +``` + +### GetOrCreatePod — Three-Tier Lookup + +When a command arrives for a session, `SessionManager.GetOrCreatePod` resolves the +target pod using a three-tier strategy: + +| Tier | Method | Latency | Description | +|------|--------|---------|-------------| +| 1 | **PodCache** | ~0ms | In-memory map of session ID → pod IP with 30s TTL | +| 2 | **Label discovery** | ~50-100ms | `List` pods by `tarsy.redhat.com/session-id` label | +| 3 | **Create** | ~3-8s | Create auth Secret + Pod, wait for readiness probe | + +Cache misses fall through to tier 2; if no pod exists, tier 3 creates one +idempotently (handles `AlreadyExists`). + +### Warm Pool + +When `--warm-pool-size N` is set, N pre-warmed pods are maintained in a ready state. +New sessions are assigned a warm pod instantly (~0ms) instead of waiting for a cold +start. Disabled by default (`N=0`). + +### Pod Spec + +Each sandbox pod runs with a hardened security context: + +- **Non-root**: UID/GID 1001, `allowPrivilegeEscalation: false`, all capabilities dropped +- **Resources**: 100m/500m CPU, 128Mi/512Mi memory +- **Readiness**: HTTP GET `/health` on port 8090 +- **Volumes**: `kubeconfig` (read-only Secret mount), `workspace` (emptyDir) +- **Auth**: HMAC-SHA256(key, sessionID) → bearer token delivered via Secret → env var + +### Cleanup + +| Method | Trigger | Description | +|--------|---------|-------------| +| **Explicit delete** | `DELETE /sessions/{id}` | TARSy calls when investigation ends. Deletes pod, Secret, and cache entry | +| **Stale cleanup** | Background ticker | Deletes pods idle > 30 minutes (via `last-activity` annotation) | + +If a sandbox pod crashes mid-investigation, shell state (env vars, working directory, +files in emptyDir) is lost. The MCP server detects the transport error, invalidates +the cache, and creates a new pod on the next call. TARSy retains the investigation +context. + ## Design Documents - [Sketch](docs/proposals/cli-mcp-server-sketch.md) — Problem statement and approach