Why it exists
Most defensive work starts the same way: a script to check one thing, then another script for a different thing, then a third that half-duplicates the first. Six months later you have twelve files with twelve different ideas about configuration, logging, and what an error looks like.
aegiscore is the answer to that drift. It provides one core — configuration, logging, module discovery, result shapes — and everything else plugs into it.
What the core handles
- Module registry. Checks declare themselves and the core discovers them. No manual import list to keep in sync.
- Uniform results. Every module returns the same structure, so reporting does not need to know what produced a finding.
- Configuration in one place. One file, validated on load, with clear failures instead of a
KeyErrorthree layers deep. - Structured logging. Machine-readable by default, human-readable when you ask for it.
- Severity as data. Findings carry a severity the reporter can sort on, rather than being encoded in a log string.
Design decisions worth explaining
Modules never talk to each other
A module receives context and returns findings. It does not reach into another module or mutate shared state. That makes any check runnable in isolation, which makes it testable, which means it actually gets tested.
Failure is a result, not a crash
A module that cannot complete returns a finding that says so. One broken check does not abort a run. In practice this is the difference between a report that is honest about gaps and a run that produced nothing.
The core stays boring
It is tempting to add convenience to the core. Every addition becomes something every module has to understand. The core does dispatch, config, logging, and results — and stops there.
Getting started
git clone https://github.com/thecrewx/aegiscore
cd aegiscore
python -m venv .venv && source .venv/bin/activate
pip install -r requirements.txt
python -m aegiscore --list
python -m aegiscore --run baseline
Where it is going
- A stable module API, so external checks can be written against a versioned contract.
- Report exporters beyond the built-in ones.
- Scheduled runs with diffing, so the interesting output is what changed.