# Phase 1: Foundation - Context

**Gathered:** 2026-02-26
**Status:** Ready for planning

<domain>
## Phase Boundary

Server endpoint + SDK scaffold (types, base classes, enums, package config) in place. Developers can install the package and the server has the new endpoint — everything needed to make an SDK call exists, even if scanners aren't implemented yet.

</domain>

<decisions>
## Implementation Decisions

### Server Endpoint Contract

- New `/api/security/scan` endpoint added to the **existing security_blueprint** (same blueprint as `/review`), not a separate blueprint
- A **new `process_sdk()` function** that accepts guardline_specs as a parameter instead of fetching from DB — separate from the existing `process()` which fetches guardlines via backend call
- **Full detail per-scanner response**: each scanner in the response includes `triggered: bool`, `detections: [{label, text, start, end, confidence, action}]`, and score
- **Both JSON and multipart/form-data support** — JSON for text scanning, multipart for future file scanning. Future-proofed from day one
- **API key validation**: security server makes a lightweight call to the backend to validate the API key before running the pipeline (same pattern as current — backend is the single source of truth for key validation)

### Authentication & Routing (Production)

- **GCP Load Balancer** routes requests by matching URL path (`/api/security/*`) + `X-Tenant-Id` header to the correct tenant's Cloud Run instance
- **Each tenant has a fully isolated Cloud Run security server** (own VPC, own SQL, own service account)
- **API key flow** (used by SDK): `X-Api-Key` + `X-Tenant-Id` headers. Security server does NOT validate the key — it forwards to the cloud backend which validates against the `security_api_keys` DB table
- **On-prem flow**: customer runs security server locally, but it still calls the cloud backend for validation. Admin overrides the security server URL from the UI
- **SDK init**: `Guard(api_key="...", tenant_id="...")` for cloud (defaults to `app.meshulash.ai`), `Guard(api_key="...", tenant_id="...", server_url="https://...")` for on-prem override
- The new `/scan` endpoint needs to validate API key via a backend call even though guardline_specs come inline

### SDK Package Structure

- Package name: `meshulash-guard` on PyPI, importable as `meshulash_guard`
- Scanners re-exported from `meshulash_guard.scanners`: `from meshulash_guard.scanners import PIIScanner, TopicScanner`
- Each scanner also importable from its own module: `from meshulash_guard.scanners.pii import PIIScanner, PIILabel`
- HTTP client: **requests** library (not httpx)
- **Sync only** for v1 — async support deferred
- Import convenience: Claude decides what's exposed at top-level `__init__.py`

### Result Type Design

- **Pydantic models** for all result types (adds pydantic as dependency)
- Detection fields: `label, text, start, end, confidence, action` — no validator info exposed in v1
- Per-scanner result access pattern: Claude decides the most ergonomic approach
- Serialization: Claude decides (pydantic's built-in `.model_dump()` is available)

### Error Handling

- **Custom exception hierarchy**: `MeshulashError` base, with `AuthError`, `ServerError`, `ConnectionError`, `ValidationError`
- **Validate API key on init**: `Guard()` constructor makes a health/validation call, raises `AuthError` early if key is invalid
- **60-second default timeout** for requests (NER + TC models can be slow on large text)
- Retry behavior: Claude decides

### Claude's Discretion

- Top-level `__init__.py` exports
- Per-scanner result access ergonomics (named attributes vs dict vs list)
- Pydantic serialization approach
- Retry behavior on transient failures
- Exact module layout beyond scanners

</decisions>

<specifics>
## Specific Ideas

- SDK should feel like `openai` or `stripe` Python packages — simple init, clean methods, structured results
- LLM Guard documentation as inspiration for how to present scanner capabilities
- The translation pattern is the core innovation: developer writes scanner config, SDK translates to guardline_specs the server already understands

</specifics>

<deferred>
## Deferred Ideas

- Async support (httpx + async methods) — future version
- File scanning through the SDK — future phase
- Normalizer registry server-side — v2

</deferred>

---

*Phase: 01-foundation*
*Context gathered: 2026-02-26*
