# Aegis Development Guide

This document outlines the core principles, architecture, and development practices for the Aegis project.

[Review adrs](docs/adrs)
-----

## Pull Requests

Developers should squash fix-up commits before requesting a review.  Fix-up commits are commits that fix mistakes (or incorporate review comments) in the commits under review.  Otherwise, we prefer commits to be smaller and focused on one thing at a time.  In particular, we strictly separate commits doing refactoring and coding style changes in preexisting code from commits that actually change the behavior of Aegis.  This makes pull requests easier to review and the resulting git history more `git bisect` friendly.

## Architecture

Aegis uses Large Language Models (LLMs) to perform advanced security analysis and operations. Its architecture is built on four key principles:

  * **Pydantic Data Models**: We enforce data integrity by using Pydantic models to define the expected structure for both LLM inputs and outputs. This provides type safety, validation, and reduces prompt engineering complexity.

  * **Autonomous Agents**: Aegis Agents, built with `pydantic-ai`, orchestrate all interactions with the LLM. They autonomously decide when and how to use available tools, making them adaptable for diverse tasks.

  * **RAG for In-Context Data**: We use Retrieval Augmented Generation (RAG) to provide private, up-to-date information to the LLM. By integrating with systems like OSIDB and a `pgvector` knowledge base, we ensure our agents have the specific context needed for accurate, relevant responses.

  * **Extensible Features**: Features are self-contained capabilities that an agent can use. They bundle a specific prompt, an output model, and any necessary logic, allowing for modular and scalable development.

-----

## Adding a New Feature

Follow these steps to create and integrate a new feature:

1.  **Define the Goal & Prompt**: Clearly define what the feature should accomplish and write a precise prompt to instruct the LLM. Test the prompt iteratively.
2.  **Define the Output Model**: Create a Pydantic `BaseModel` that defines the structure of the data you expect back from the LLM.
3.  **Implement the Feature**: Add a new module under the `src/aegis/features/` directory that combines the prompt and the Pydantic output model.
4.  **Provide Context (If Needed)**: If the feature requires external data, either integrate a new tool or add relevant documents to the RAG knowledge base.
5.  **Write Tests & Expose**: Add unit tests for the feature. Expose the new capability through the project's CLI and/or REST API.

-----

## Getting Started

Aegis development is managed with **`uv`**, a fast Python package installer and resolver.

#### \#\#\# Setup

1.  **Install `uv`**:

    ```bash
    pip install uv
    ```

2.  **Sync Dependencies**: Install all project dependencies, including development tools. `uv` will create and manage a virtual environment automatically in `.venv`.

    ```bash
    uv sync --all-extras
    ```

#### \#\#\# Running Aegis

  * **Run a Script**:
    ```bash
    uv run python scripts/<script_name>.py
    ```
  * **Start the REST API**:
    ```bash
    uv run uvicorn src.aegis_ai_web.src.main:app --port 9000
    ```
  * **Launch the CLI**:
    ```bash
    uv run aegis --help
    ```

#### \#\#\# Managing Dependencies

  * **Add a New Dependency**:
    ```bash
    uv add numpy
    ```
  * **Add a Development-Only Dependency**:
    ```bash
    uv add --dev mypy
    ```

-----

## Code Quality

We enforce code quality using **`ruff`** for linting and formatting. These checks are run automatically in CI.

  * **Check for Linting Errors**:
    ```bash
    uvx ruff check .
    ```
  * **Apply Formatting**:
    ```bash
    uvx ruff format .
    ```

-----

## Configuration

Configure Aegis via environment variables, typically loaded from a `.env` file in the project root. For LLM authentication, set `OPENAI_API_KEY` or `ANTHROPIC_API_KEY` as environment variables.

**Example `.env` file:**

```ini
# AEGIS CLI default agent
AEGIS_CLI_FEATURE_AGENT="redhat"

# LLM Connection Details
AEGIS_LLM_HOST="https://generativelanguage.googleapis.com"
AEGIS_LLM_MODEL="gemini-2.5-pro"
GEMINI_API_KEY=XXXXXXXXXXXXXXXXXXXXXXXXXXXXX

# Enable OTEL
AEGIS_OTEL_ENABLED="true"
OTEL_EXPORTER_OTLP_ENDPOINT='http://localhost:4318'

# Recapture LLM results to cache in tests
TEST_ALLOW_CAPTURE="true"

# Tooling API Keys and URLs
NVD_API_KEY="XXXXXXXXXXXXXXXXXXXXXXX"
TAVILY_API_KEY="tvly-dev-XXXXXX"

# OSIDB bindings conn details
AEGIS_OSIDB_SERVER_URL="https://osidb-stage.example.com"
AEGIS_OSIDB_RETRIEVE_EMBARGOED=false

# Required for environments with custom SSL certificates
REQUESTS_CA_BUNDLE="/etc/pki/tls/certs/ca-bundle.crt"


```

-----

## Testing

Our test suite uses **`pytest`** and `pytest-asyncio`.

  * **Run All Tests**:
    ```bash
    make test
    ```
  * **Run a Specific Test by Name**:
    ```bash
    uv run pytest -k "test_suggest_impact_with_bad_cve"
    ```

### Feedback Log Analysis

The web API logs user feedback directly to a CSV file (`feedback.csv`), which preserves all original data including special characters. Special characters (commas, quotes, newlines) are automatically escaped by Python's CSV library.

  * **CSV Format**: Standard CSV with headers and proper escaping:
    ```csv
    datetime,feature,cve_id,actual,expected,request_time,accept
    2025-11-20 13:07:26.894,suggest-impact,CVE-2025-23395,IMPORTANT,CRITICAL,,False
    2025-11-20 13:07:30.123,suggest-cwe,CVE-2025-12345,"Value, with comma","Quote ""safety"" test",,True
    ```

  * **Analyze Feedback**: Use standard CSV tools:
    ```bash
    # Count by feature
    tail -n +2 feedback.csv | cut -d, -f2 | sort | uniq -c
    
    # Open in spreadsheet
    libreoffice feedback.csv
    ```

-----

## Registering MCP

Add MCP servers to aegis_ai.toolsets

-----

## Building and running container

To simply build container:
```commandline
podman build -f Containerfile -t aegis-ai
```

Build args can be passed in as well:
```commandline
podman build --build-arg PIP_INDEX_URL="${PIP_INDEX_URL}" \
		     --build-arg RH_CERT_URL=${RH_CERT_URL} \
		     --tag aegis-ai .
```

To run built container:
```commandline
podman run --rm -it -p 9000:9000 localhost/aegis-ai:latest
```

Optionally one can set krb5 config as well:
```commandline
podman run --rm -it -v /etc/krb5.conf:/etc/krb5.conf -p 9000:9000 localhost/aegis-ai:latest
```

-----

## Publishing & Releasing

####  Making a Release

Aegis uses semantic versioning for stable releases and `hatch-vcs` for development snapshots.

1.  **Update Changelog**: Add the release notes to `docs/CHANGELOG.md`.
2.  **Sync Lockfile**: Update `uv.lock` by running `make` or `uv lock`.
3.  **Submit PR**: Create a pull request, get it reviewed, and merge it into the `main` branch.
4.  **Merge PR**: Merge the pull request to the `main` branch.  This will trigger a new build of a container image that will be tagged into `:latest` and automatically deployed on the staging environment.
5.  **Test the release candidate**: Test the automatically deployed image in the staging environment.
6.  **Push a git tag**: Create a new signed git tag (e.g. `0.3.0`) and push it to the upstream git repository on GitHub.  This will trigger a new build of a container image that will be tagged into `:stable` (and e.g. `:0.3.0`) and automatically deployed on the production environment.
6.  **Create a GitHub Release**: Create a new release on GitHub from the git tag.


#### Build and Publish to PyPI

1.  **Build the Distribution**:
    ```bash
    make build-dist
    ```
2.  **Publish to PyPI**: Set your credentials and run the publish command.
    ```bash
    export TWINE_USERNAME=__token__
    export TWINE_PASSWORD=pypi-your-long-api-token-string-here

    make publish-dist
    ```
