---
phase: 01-foundation
plan: 01
subsystem: api
tags: [flask, python, security-server, guardlines, sdk, pipeline, text-classification]

# Dependency graph
requires: []
provides:
  - POST /api/security/scan endpoint with inline guardline_specs and per-scanner JSON response
  - GET /api/security/health/auth endpoint for API key validation
  - process_sdk() function running 5-stage pipeline without DB guardline fetch
  - _build_tc_policies_from_specs() converting inline guardline_specs to TCRecognizer format
  - _validate_api_key_via_backend() shared helper for backend API key validation
affects:
  - 01-02 (SDK client will call these new endpoints)
  - 02-scanners (scanner implementations will depend on the per-scanner response contract)

# Tech tracking
tech-stack:
  added: []
  patterns:
    - "SDK scan endpoint bypasses DB fetch: accepts inline guardline_specs instead of fetching from backend"
    - "Backend key validation: lightweight GET to /api/guardlines/with-api-key to validate API key"
    - "Per-scanner response: gating_result.triggered_guardlines drives scanners dict in response"

key-files:
  created: []
  modified:
    - /Users/shalev/Desktop/AIM/security-server/src/main.py

key-decisions:
  - "Explicit backend API key validation in scan_sdk() since get_policies() is bypassed"
  - "import requests as requests_lib at module level to avoid name collision with flask.request"
  - "process_sdk() uses local imports to mirror existing process_text() style and avoid circular imports"
  - "_build_tc_policies_from_specs() groups TC entries by head to match _adapt_tc_policies() output format"

patterns-established:
  - "Per-scanner breakdown: {glid: {triggered, score, detections}} keyed by guardline ID"
  - "5-stage pipeline reuse: process_sdk() calls same stages as process_text() but with inline specs"

# Metrics
duration: 2min
completed: 2026-02-26
---

# Phase 1 Plan 1: Add /api/security/scan and /api/security/health/auth Summary

**Flask SDK scan endpoint with inline guardline_specs, 5-stage pipeline execution, and per-scanner JSON breakdown for PII/TC/regex detection**

## Performance

- **Duration:** 2 min
- **Started:** 2026-02-26T16:45:24Z
- **Completed:** 2026-02-26T16:47:36Z
- **Tasks:** 1
- **Files modified:** 1

## Accomplishments

- Added `POST /api/security/scan` endpoint: accepts JSON body with `text` + `guardline_specs`, runs the full 5-stage pipeline without fetching guardlines from the DB, returns per-scanner breakdown
- Added `GET /api/security/health/auth` endpoint: validates API key via backend, returns 200/401 — used by SDK `Guard.__init__()` to validate credentials at construction time
- Added `process_sdk()` function that runs normalize → recognize (NER + regex + TC) → validate → score → gate → act with inline guardline_specs
- Added `_build_tc_policies_from_specs()` to convert inline guardline_specs into the `tc_policies` list format that `TCRecognizer.classify()` requires (groups by head, sets thresholds)
- Added `_validate_api_key_via_backend()` shared helper that makes a lightweight GET to the backend to validate the API key (since `get_policies()` DB fetch is bypassed in the SDK flow)

## Task Commits

Each task was committed atomically:

1. **Task 1: Add process_sdk() function and /api/security/scan endpoint** - `096cdef` (feat)

**Plan metadata:** (docs commit follows)

## Files Created/Modified

- `/Users/shalev/Desktop/AIM/security-server/src/main.py` - Added `_build_tc_policies_from_specs()`, `process_sdk()`, `_validate_api_key_via_backend()`, `/api/security/scan` route, `/api/security/health/auth` route; added `import requests as requests_lib` and `from src.env import BACKEND_URL, TIMEOUT`

## Decisions Made

- **Explicit backend API key validation:** Since `scan_sdk()` bypasses `get_policies()` (which would implicitly validate the key by fetching guardlines), an explicit lightweight GET to `BACKEND_URL/api/guardlines/with-api-key` is made before processing. This validates the key without using the returned guardlines.
- **`import requests as requests_lib` at module level:** `flask.request` is already in scope; importing `requests` (the HTTP library) as `requests_lib` at module level avoids the name collision and is consistent with the library import alias used elsewhere.
- **Local imports inside `process_sdk()`:** Following the existing pattern in `text.py` (e.g., `from src.logger import logger` inside functions) to keep import style consistent and avoid potential circular dependency issues.
- **`_build_tc_policies_from_specs()` groups by head:** The `_adapt_tc_policies()` function in `policies.py` produces one dict per unique head. The new helper mirrors this format so `TCRecognizer.classify()` receives the expected structure.

## Deviations from Plan

None - plan executed exactly as written.

## Issues Encountered

- `env.py` raises `AttributeError` if `BACKEND_URL` env var is not set (it calls `.rstrip("/")` on the result of `os.getenv()`). This is pre-existing behavior — the server requires `BACKEND_URL` to be set at startup. Not a bug introduced by this plan; documented for context. Verification was run with `BACKEND_URL=http://localhost:8080` set.

## User Setup Required

None - no external service configuration required for the server changes themselves. The new endpoints require the same `BACKEND_URL` env var already required by the server.

## Next Phase Readiness

- `/api/security/scan` is ready for the SDK client (Plan 01-02) to call
- `/api/security/health/auth` is ready for SDK `Guard.__init__()` to call
- Per-scanner response contract is defined: `{status, processed_text, placeholders, level, scanners, risk_categories}`
- No blockers for Phase 1 Plan 2 (SDK core)

---
*Phase: 01-foundation*
*Completed: 2026-02-26*
