# Architecture ## Component map Architecture decision records live in [docs/adr](./adr/README.md). ```text MCP client | stdio or Streamable HTTP v src/mcp.ts / src/server-http.ts | +-- src/http-security.ts HTTP host/origin/auth/body-limit policy +-- src/server-core.ts MCP tool registration and handlers +-- src/collector.ts SSH metric collection and parsing +-- src/analyzer.ts Threshold and z-score anomaly analysis +-- src/baseline.ts SQLite snapshot persistence and history +-- src/db.ts SQLite connection and schema setup +-- src/ssh.ts SSH sessions and strict host verification +-- src/logging.ts Structured stderr logging and redaction +-- src/observe.ts Standalone OpenMetrics and OTLP runtime +-- src/observability-* Read-only metric conversion and scrape policy +-- src/otlp-metrics.ts OTLP/HTTP JSON encoding and export +-- src/types.ts Zod schemas and shared TypeScript types ``` ## Data flow 1. The MCP client calls `analyze_server_snapshot` for an immediate pass or `analyze_server` for a bounded sampled window. 2. `server-core.ts` validates input with Zod. In remote-safe profiles, raw SSH credential fields are omitted from the schema. 3. `collector.ts` opens an SSH session through `ssh.ts`. 4. `ssh.ts` verifies the remote host key by pinned SHA256 fingerprint or `known_hosts` before the connection is accepted. 5. `collector.ts` gathers CPU from `/proc/stat` deltas, memory pressure from available memory, disk usage, optional network metrics, optional process metrics, and OS metadata. 6. `analyzer.ts` loads only approved baseline samples and evaluates the current snapshot before it is persisted. 7. `baseline.ts` stores the evaluated snapshot as an `observation`; only `record_baseline` writes `baseline` records. SSH credentials are never persisted. 8. Sampled analysis reports token-bound MCP progress. Cancellation propagates through the sample delay and SSH lifecycle; only a completed analysis is persisted and returned. 9. Tool responses are readable JSON text plus schema-validated `structuredContent`. ## Observability export flow `infra-lens-observe` is separate from the MCP HTTP transport. It reads one latest `observation` row per host, validates the stored JSON, converts bounded numeric fields to a shared metric model, and either renders OpenMetrics or sends OTLP/HTTP JSON. Scrapes never trigger SSH collection and baseline rows are excluded. The listener is disabled by default, binds to loopback by default, and requires explicit remote opt-in. See [Observability exports](./observability.md). ## HTTP transport `server-http.ts` uses the MCP SDK Streamable HTTP transport and a local request policy layer: - loopback bind by default - fail-fast validation for non-loopback binds - optional bearer fallback for local/dev use - OAuth protected-resource metadata endpoint - explicit Host and Origin allowlists - JSON body size limit - sanitized JSON errors OAuth token validation is intentionally left to an external gateway for production deployments. ## Database schema ```sql CREATE TABLE snapshots ( id INTEGER PRIMARY KEY AUTOINCREMENT, host TEXT NOT NULL, label TEXT NOT NULL DEFAULT 'default', classification TEXT NOT NULL DEFAULT 'observation' CHECK (classification IN ('observation', 'baseline')), timestamp INTEGER NOT NULL, cpu_percent REAL NOT NULL, memory_percent REAL NOT NULL, load_1 REAL NOT NULL, raw_json TEXT NOT NULL ); ``` SQLite `PRAGMA user_version` tracks schema version 1. During migration from the legacy schema, ambiguous `default` rows become observations and explicitly named rows become baseline samples. This conservative rule prevents historical incident data from silently becoming an approved healthy baseline. ## Release architecture GitHub Actions is the release automation surface: - `ci.yml` runs format, lint, typecheck, tests, metadata checks, package dry-run, and Docker smoke. - `security.yml` runs workflow lint, workflow security lint, secret scan, dependency audit, Trivy, and Scorecard. - `osv-scanner-pr.yml` blocks pull requests that introduce a new `pnpm-lock.yaml` vulnerability. - `osv-scanner-full.yml` performs fail-closed full lockfile scans on `main`, weekly, and by manual dispatch with SARIF upload. - `codeql.yml` runs CodeQL for JavaScript and TypeScript. - `release.yml` runs release-please manifest mode and publishes only when release-please creates a release. - `review-thread-gate.yml` blocks actionable unresolved PR review threads.