Provider-agnostic Identity Threat Detection & Response (ITDR) platform. Ingests authentication signals from Clerk, Auth0, and a native Python SDK; correlates them into security incidents; and executes automated multi-provider containment with a tamper-evident cryptographic audit trail.
- Project Overview
- Why BoundaryGuard Exists
- Core Capabilities
- Technology Stack
- Repository Structure
- Documentation
- Quick Start
- Project Status
- Contributing
- Security
- License
BoundaryGuard is a provider-agnostic Identity Threat Detection & Response (ITDR) platform. It ingests authentication signals from Clerk, Auth0, and a native Python SDK; classifies and correlates those signals into security incidents; and executes automated multi-provider containment when an incident requires a response.
Who it is for:
- Security engineers building programmatic incident-response workflows on top of external identity providers
- Platform teams that need identity threat detection embedded directly into customer-facing applications via an SDK
- Operators who require a single normalized, tamper-evident audit record of every remediation action across all providers
Modern applications delegate authentication to external providers (Clerk, Auth0, and similar). Those providers handle credential issuance. They are not designed to:
- Detect credential abuse after issuance
- Revoke an active session across all devices in a single API call
- Provide a normalized, auditable view of events across multiple providers simultaneously
When an account is compromised — session hijacking, MFA fatigue, impossible-travel anomaly — the response path is manual: pull audit logs from the provider console, cross-reference with application logs, decide to revoke, then navigate provider-specific admin UIs to execute. Each step is a separate tool; none interoperate under time pressure.
BoundaryGuard closes that gap:
| Problem | Response |
|---|---|
| Auth telemetry flows into provider-side logs with no normalized ingestion path | POST /api/v1/telemetry/ingest accepts Clerk webhooks, Auth0 log-stream payloads, and native SDK events through a single HMAC-verified endpoint; each is normalized to NormalizedSecurityEvent before enqueueing |
| No single revocation call reaches all active sessions across all providers | MitigationOrchestrator dispatches to ClerkConnector, Auth0Connector, or CustomJWTConnector based on auth_provider; each connector executes the provider-specific revocation protocol |
| The SDK has no way to enforce revocation at the application edge | BlocklistMiddleware checks blocklist:{tenant_id}:{family_id} in Redis on every request; revoked families receive 401 {"detail":"Token family revoked"} before the downstream application is reached |
| No tamper-evident audit record of who revoked what and when | mitigation_logs is an append-only table (REVOKE UPDATE, DELETE FROM public) with a SHA-256 hash chain: parent_hash → payload_hash → signature computed by _write_mitigation_log in worker/main.py |
All capabilities listed are implemented in the current codebase.
| Capability | Details |
|---|---|
| Multi-provider telemetry ingestion | Single POST /api/v1/telemetry/ingest endpoint accepts Clerk webhooks, Auth0 log-stream payloads, and native SDK events; source is discriminated by payload structure before schema validation; all paths return 202 Accepted with async SQS enqueue |
| Deterministic severity classification | classify_event takes event_type baseline (LOW → CRITICAL per known type) and severity_score in [0.0, 1.0]; result = max(baseline, score_tier); unknown event types default to MEDIUM |
| Incident correlation | 15-minute sliding window; events sharing (tenant, account, trigger_type) append to an open incident and escalate severity if the new event warrants a higher tier (SELECT … FOR UPDATE on the chain tip) |
| Multi-provider automated containment | ClerkConnector — POST /v1/sessions/{id}/revoke; Auth0Connector — DELETE /api/v2/users/{id}/sessions with client-credentials token caching; CustomJWTConnector — Redis SET blocklist:{tenant}:{session_id} + PUBLISH revocations:{tenant} |
| Fail-open SDK blocklist middleware | L1 in-process LRU cache (4 096 entries, no TTL) → L2 BlocklistRedisReader (15 ms asyncio.wait_for hard ceiling) → Pub/Sub proactive invalidation via RevocationSubscriber; adapters for Starlette, FastAPI, and Django; every failure path returns False (allow) |
| Tamper-evident cryptographic audit chain | mitigation_logs is append-only (REVOKE UPDATE, DELETE FROM public); each row stores parent_hash, payload_hash, and signature (SHA-256); chain verified offline with scripts/verify_audit_chain.py |
| Real-time SSE incident stream | GET /api/incidents/stream pushes incident events to the dashboard over Server-Sent Events; backed by incident_updates:{tenant_id} Redis pub/sub channel |
| RBAC | Three roles: viewer, responder, security_admin; enforced server-side on every action route and in the dashboard kill-switch panel |
| Idempotent freeze action | Idempotency-Key UUID v4 deduplication via Redis (24 h TTL); replayed requests return the cached result without re-executing containment |
Backend
- Python 3.11+, FastAPI, Uvicorn
- SQLAlchemy 2.x (asyncio) + asyncpg, Alembic
- Pydantic v2, pydantic-settings
- redis.asyncio, aioboto3 / boto3, httpx
SDK
- Python 3.11+
- ASGI middleware adapters: Starlette, FastAPI, Django
- redis.asyncio (blocklist reader + pub/sub subscriber)
Dashboard
- Next.js 15, TypeScript, TailwindCSS
- Clerk (authentication, session management)
- React Query, Vitest
Infrastructure
- Terraform — AWS ECS Fargate, ALB, WAF v2 (OWASP Top 10), ACM TLS
- AWS SQS — standard queue (
incident-events-queue) + FIFO queue (outbound-webhooks.fifo) - PostgreSQL 16 — RDS Multi-AZ, SSL
verify-full - Redis 7 — ElastiCache, TLS + AUTH token
- AWS Secrets Manager, S3 Object Lock COMPLIANCE (365 d), CloudTrail, GuardDuty, SNS
Testing
- pytest + pytest-asyncio (Python services)
- Playwright (dashboard E2E)
- Vitest (dashboard unit + integration)
| Path | Role |
|---|---|
services/core-api |
Control plane. Incident lifecycle management, RBAC-gated freeze execution (POST /v1/actions/freeze), real-time SSE streaming (GET /v1/incidents/stream), and an append-only audit_events table with SHA-256 hash chain. Python 3.11+, FastAPI, SQLAlchemy asyncio, asyncpg. |
services/ingestion-api |
Telemetry ingestion. POST /api/v1/telemetry/ingest verifies the x-identityops-signature HMAC-SHA256 header, discriminates source (Clerk / Auth0 / native SDK) by payload structure, validates against the appropriate Pydantic schema, normalizes to NormalizedSecurityEvent, and enqueues to SQS as a background task. |
services/worker-incident |
Incident worker. SQS consumer that classifies each event (classify_event: event-type baseline + anomaly score → LOW/MEDIUM/HIGH/CRITICAL), correlates it against open incidents within a 15-minute window (correlate_event with SELECT … FOR UPDATE), persists to PostgreSQL, and publishes an incident_updates:{tenant_id} Redis notification. |
services/worker-webhooks |
Containment worker. SQS consumer that routes each freeze event through MitigationOrchestrator to the correct connector (ClerkConnector, Auth0Connector, CustomJWTConnector), delivers outbound webhooks with exponential-backoff retry and DLQ, and appends a hash-chained row to mitigation_logs. |
packages/sdk-python |
Client SDK. IdentityOpsClient for telemetry submission; BlocklistMiddleware for request-time enforcement (Starlette, FastAPI, Django); LRUCache + BlocklistRedisReader (15 ms timeout) + RevocationSubscriber for real-time blocklist state. All failure paths are fail-open. |
apps/dashboard |
Operator console. Next.js application with Clerk authentication, per-tenant incident list, live SSE stream (useIncidentStream), kill-switch freeze panel, and RBAC-gated actions (viewer / responder / security_admin). |
| Document | Contents |
|---|---|
| INSTALL.md | Local development setup from clean clone — one-command bootstrap (scripts/dev.sh / scripts/dev.ps1), manual step-by-step guide, environment variable blocks, migrations, seed scripts, health checks, troubleshooting table |
| ARCHITECTURE.md | Control vs. data plane separation, SDK hot path (LRU → Redis → Pub/Sub), multi-provider connector loop, cryptographic audit chain, four design decisions with trade-offs, full Mermaid sequence diagram, enterprise scaling notes |
| OPERATIONS.md | Local development setup, full-stack startup sequence, environment variable reference for all five Python services, operational verification commands, incident response simulation, troubleshooting guide, production deployment notes |
| THREAT_MODEL.md | STRIDE analysis per component, trust boundary definitions, attack surfaces, mitigation mapping, SSE abuse analysis, replay attack mitigation, revocation race-condition analysis, fail-open reasoning, accepted risks register — security-sensitive |
| Document | Contents |
|---|---|
| CONTRIBUTING.md | Contribution workflow, branch conventions, commit format, PR checklist |
| SECURITY.md | Vulnerability disclosure policy, supported versions, response SLA |
| ROADMAP.md | Public milestone roadmap |
| CHANGELOG.md | Version history (Keep a Changelog format) |
Full setup instructions (one-command bootstrap, manual step-by-step, environment variables, health checks) are in INSTALL.md.
# Linux / macOS / WSL / Git Bash
bash scripts/dev.sh
# Windows PowerShell
.\scripts\dev.ps1Handles Docker, pip installs, migrations, seed data, npm install, and starts all services in the background. Exits when every service is confirmed healthy. See INSTALL.md §0 for --no-seed and --stop flags.
Requires four long-running terminals open from the repository root, plus one setup step.
No
.envfiles are needed. All services default tolocalhost:5432(PostgreSQL),localhost:6379(Redis), andlocalhost:4566(LocalStack SQS). Override only when connecting to non-default hosts.
Terminal 1 — Infrastructure (returns immediately — leave Docker running)
docker compose -f docker/docker-compose.dev.yml up -d
# Confirm all three containers are healthy before starting services:
docker compose -f docker/docker-compose.dev.yml psTerminal 2 — core-api (port 8000)
cd services/core-api
pip install -e ".[test]" && alembic upgrade head
uvicorn main:app --reload --port 8000Terminal 3 — ingestion-api (port 8001)
cd services/ingestion-api
pip install -e ".[test]"
uvicorn identityops.main:app --reload --port 8001Terminal 4 — worker-incident
cd services/worker-incident
pip install -e ".[test]"
python -m worker.mainTerminal 5 — worker-webhooks
cd services/worker-webhooks
pip install -e ".[test]"
python -m worker.mainSetup — seed data and dashboard (run from repository root once services are healthy)
# Seed the Acme SaaS tenant (required for dev auth mode)
python scripts/seed_dev_data.py
# Create apps/dashboard/.env.local (content shown below), then:
cd apps/dashboard && npm ci && npm run dev
# Open http://localhost:3000apps/dashboard/.env.local — create this file in your editor or IDE:
ENABLE_DEV_AUTH=true
NEXT_PUBLIC_CORE_API_URL=http://localhost:8000/v1The dashboard opens directly to the incident inbox. A yellow Dev Auth Active badge confirms dev mode is active. Browse incidents, execute freeze actions, and view audit logs without a Clerk account.
Two scripts drive a complete local demonstration without any external credentials.
# 1. Seed the demo tenant (NexaCorp) with realistic data
# Creates accounts, token families, historical incidents, mitigation logs,
# and a SHA-256 chained audit trail.
python scripts/demo_data.py
# 2. Run the end-to-end simulation
# Requires all services from the Manual path above to be running.
python scripts/demo_simulation.pyThe simulation pipeline:
generate telemetry → submit (HMAC-signed POST /v1/events)
→ SQS → worker-incident → incident created
→ GET /v1/incidents (poll) → freeze executed (POST /v1/actions/freeze)
→ DB committed → Redis blocklist set → SQS outbound-webhooks
→ worker-webhooks → MitigationLog written
→ audit chain verified (SHA-256)
Expected final output: Demo completed successfully.
# Integration tests — run from services/core-api/
cd services/core-api && pytest -q
# 243 passed, 5 skipped
# End-to-end containment simulation
cd services/core-api && pytest ../../tests/simulation/ -v
# 22 passed
# worker-webhooks unit tests
cd services/worker-webhooks && pytest tests/ -qPlaywright E2E tests (apps/dashboard/e2e/) require CLERK_SECRET_KEY and skip automatically without it.
Based on repository evidence:
- Core pipeline is implemented end-to-end: ingest → classify → correlate → containment → cryptographic audit
- Test coverage: 243 integration tests + 22 end-to-end containment simulation tests pass; 35 Playwright E2E tests exist and skip cleanly without live Clerk credentials
- Production infrastructure: Terraform-defined on AWS — ECS Fargate, RDS Multi-AZ, ElastiCache Redis TLS, WAF v2 OWASP, S3 Object Lock COMPLIANCE (365 d), CloudTrail multiregion, GuardDuty
- CI security pipeline (
.github/workflows/security.yml): Trivy filesystem, Trivy container image scanning (4 services), Semgrep SAST, Trivy IaC (Terraform), Gitleaks, pip-audit, npm-audit, security gate job - Package names (
identityops-*) carryTODO(major-release)comments and will be renamed toboundaryguard-*in a future major version; existing import paths and runtime identifiers are unchanged for backward compatibility
See CONTRIBUTING.md for the development setup, coding standards, branch strategy, commit conventions, and pull request checklist.
To report a vulnerability, see SECURITY.md. Do not open a public GitHub issue for security vulnerabilities.
For the threat model, accepted risk register, and fail-open design rationale, see THREAT_MODEL.md.
MIT — see LICENSE.
For architecture internals, see ARCHITECTURE.md. For operational procedures, see OPERATIONS.md.