ARGUS — ArgusLabs

Architecture

System design, file structure, and extension points.

System Overview

ARGUS is structured as a layered system: instrumentation at the bottom, detection in the middle, and presentation at the top. Each layer is independent — you can swap storage backends, add custom detectors, or build your own UI on top of the trace API.

ARGUS system architecture: instrumentation layer → storage → detection engine → forensics → presentation (CLI, UI, API)
Layered architecture: instrumentation → detection → presentation

Data Flow

Here's what happens during a watched execution, step by step:

  1. Instrumentationwatcher.watch(graph)patches the graph's node callbacks to intercept inputs and outputs at each step
  2. Execution recording — as the pipeline runs, each node execution creates a TraceStep with input state, output state, timing, and metadata
  3. Detectionwatcher.finalize() passes the collected trace through all four detection layers in parallel
  4. Forensics — if detections are found andinvestigate is enabled, the forensic analyzer traces the causal chain from symptom to root cause
  5. Storage — the complete trace (steps + detections + forensics) is written to the SQLite database
  6. Presentation — results are available via CLI commands, the local UI, or the Python API

Extension Points

ARGUS is designed to be extended. The main extension points:

  • Custom detectors — implement the detector interface to add domain-specific detection logic
  • Storage backends — extend the base storage class to persist traces to PostgreSQL, S3, or any other backend
  • Validators — pass custom validation functions via the validators parameter for domain-specific structural checks
  • Report formats — the report generator supports custom templates for HTML and Markdown output
python
# Example: Custom detector
from argus.detectors.base import BaseDetector

class MyDetector(BaseDetector):
    def detect(self, trace):
        detections = []
        for step in trace.steps:
            if self.check_something(step):
                detections.append(
                    Detection(
                        layer="custom",
                        severity="warning",
                        message="Custom check failed",
                        step_id=step.id,
                    )
                )
        return detections