Architecture Overview¶
Sentinel DV is designed with security-first principles and strict separation of concerns.
Design Principles¶
1. Read-Only by Design¶
No mutations, no control:
- All tools are strictly read-only
- No simulator triggers or job submissions
- No artifact modification
- No configuration changes via API
Enforcement:
# All file operations use read-only modes
with open(artifact_path, 'r') as f: # Never 'w' or 'a'
data = f.read()
2. Schema-First Contracts¶
Every response conforms to versioned schemas:
from sentinel_dv.schemas import TestCase
# Response structure is deterministic
test: TestCase = {
"id": "T123",
"framework": "uvm",
"status": "fail",
...
}
Benefits:
- LLMs can reason reliably
- Backward compatibility guarantees
- Self-documenting APIs
- Testable contracts
3. Deterministic Outputs¶
Same input → same output:
- No LLM-generated fields in responses
- All facts derived from artifacts
- Stable signature hashing
- Reproducible indexing
4. Evidence-Based Facts¶
Every fact traceable to source:
failure: FailureEvent = {
"message": "AXI BRESP error",
"evidence": [
{
"kind": "log",
"path": "test.log",
"span": {"start_line": 142, "end_line": 148},
"extract": "...",
"hash": "sha256..."
}
]
}
5. Security Boundaries¶
Defense in depth:
- Path sandboxing - Only configured roots accessible
- Automatic redaction - Secrets/PII removed
- Size limits - Prevent DoS and prompt injection
- Input validation - Pydantic schemas
- Fail-safe defaults - Conservative limits
Component Architecture¶
┌─────────────────────────────────────────────────────────┐
│ MCP Server │
│ (server.py + registry.py) │
│ - Tool registration │
│ - Request routing │
│ - Response serialization │
└──────────────────┬──────────────────────────────────────┘
│
┌──────────────────▼──────────────────────────────────────┐
│ Tools Layer │
│ runs.py │ tests.py │ failures.py │ assertions.py │... │
│ - Business logic │
│ - Query construction │
│ - Pagination & filtering │
└──────────────────┬──────────────────────────────────────┘
│
┌──────────────────▼──────────────────────────────────────┐
│ Indexing Layer │
│ indexer.py │ store.py │ query.py │
│ - Artifact scanning │
│ - DuckDB queries │
│ - Caching & optimization │
└──────────────────┬──────────────────────────────────────┘
│
┌──────────────────▼──────────────────────────────────────┐
│ Adapters Layer │
│ uvm_log.py │ cocotb.py │ assertions.py │ coverage.py │
│ - Parse raw artifacts │
│ - Normalize to schemas │
│ - Extract metadata │
└──────────────────┬──────────────────────────────────────┘
│
┌──────────────────▼──────────────────────────────────────┐
│ Normalization Layer │
│ signatures.py │ taxonomy.py │ redaction.py │
│ - Failure signature hashing │
│ - Category mapping │
│ - Automatic redaction │
└──────────────────┬──────────────────────────────────────┘
│
┌──────────────────▼──────────────────────────────────────┐
│ Schemas Layer │
│ common.py │ tests.py │ failures.py │ assertions.py │...│
│ - Pydantic models │
│ - Validation rules │
│ - Type definitions │
└─────────────────────────────────────────────────────────┘
Data Flow¶
Indexing Flow¶
Steps:
- Scan configured artifact roots
- Parse files using enabled adapters
- Normalize extracted data (redaction, signatures)
- Store in indexed database
- Build lookup indexes for fast queries
Query Flow¶
MCP Request → Tool → Query Builder → Index Store → Normalization → Response
↓ ↓ ↓ ↓ ↓
Validate SQL/Filter DuckDB Redact Schema
Input Construction Query Evidence Serialize
Steps:
- Receive MCP tool call
- Validate input parameters
- Build database query with filters/pagination
- Execute query against index
- Normalize results (redaction, bounding)
- Serialize to schema-compliant JSON
Storage Architecture¶
DuckDB Schema¶
-- Runs table
CREATE TABLE runs (
run_id TEXT PRIMARY KEY,
suite TEXT,
created_at TIMESTAMP,
ci_system TEXT,
ci_job_url TEXT,
ci_build_id TEXT
);
-- Tests table
CREATE TABLE tests (
id TEXT PRIMARY KEY,
run_id TEXT,
framework TEXT,
name TEXT,
status TEXT,
duration_ms INTEGER,
seed INTEGER,
FOREIGN KEY (run_id) REFERENCES runs(run_id)
);
-- Failures table
CREATE TABLE failures (
id TEXT PRIMARY KEY,
test_id TEXT,
category TEXT,
severity TEXT,
summary TEXT,
message TEXT,
time_ns INTEGER,
signature_id TEXT,
FOREIGN KEY (test_id) REFERENCES tests(id)
);
-- ... additional tables for assertions, coverage, topology
Indexes¶
-- Performance indexes
CREATE INDEX idx_tests_run_id ON tests(run_id);
CREATE INDEX idx_tests_status ON tests(status);
CREATE INDEX idx_failures_test_id ON failures(test_id);
CREATE INDEX idx_failures_signature ON failures(signature_id);
CREATE INDEX idx_failures_category ON failures(category);
Security Architecture¶
Defense Layers¶
- Configuration Validation
- Schema-based validation
- Path existence checks
-
Permission verification
-
Path Sandboxing
- Normalized paths only
- No
..traversal -
Artifact root allowlist
-
Automatic Redaction
- Credential patterns
- Email addresses
- Absolute paths
-
IP addresses
-
Response Bounding
- Max response size (2MB default)
- Max items per page (200 default)
- Max evidence refs (10 default)
-
Max excerpt length (1KB default)
-
Input Validation
- Pydantic models
- Enum validation
- Range checks
Redaction Pipeline¶
Raw Text → Pattern Matching → Replacement → Truncation → Output
↓ ↓ ↓
Regex Scan <REDACTED> Max Length
Extensibility¶
Adding a New Adapter¶
- Create
adapters/new_tool.py - Implement parser functions returning schema objects
- Add enable flag to
AdaptersConfig - Write tests with fixtures
- Update documentation
Adding a New Tool¶
- Create
tools/new_tool.py - Define request/response schemas
- Implement query logic
- Register in
registry.py - Document in tool reference
Performance Considerations¶
Indexing¶
- Incremental indexing for large artifact sets
- Parallel parsing of independent files
- Deduplication of identical artifacts
Querying¶
- Selective projection - fetch only needed fields
- Smart pagination - stable ordering
- Query optimization - DuckDB query planner
- Connection pooling - reuse database connections