Modular AI security platform with pluggable analysis modules: a Python core exposing a Typer CLI and a FastAPI REST API, backed by PostgreSQL with pgvector and an ARQ/Redis task queue, paired with a React + Vite + TypeScript frontend.
+-------------------------------------------------------------+
| Frontend (frontend/) |
| React 19 + Vite + TypeScript shell |
| Module UIs mounted from modules/<id>/frontend/ |
+----------------------------+--------------------------------+
| HTTP / SSE / JWT
+----------------------------v--------------------------------+
| API (src/aila/api/) |
| FastAPI app with 29 platform routers + module-contributed |
| routers, JWT auth, RBAC, |
| SSE event streams, OpenAPI at /docs |
+----------------------------+--------------------------------+
|
+--------------------+--------------------+
| |
+-------v-------------+ +--------v-----------+
| Platform | | Modules |
| src/aila/platform/ | | src/aila/modules/ |
| | | |
| routing/ | | forensics/ |
| runtime/ | <-- ModuleProto. | hello_world/ |
| services/ | contracts -> | vr/ |
| contracts/ | | vulnerability/ |
| tools/ | | |
| llm/ | | |
| tasks/ (ARQ) | | Each module owns |
| workflows/ | | its own runtime, |
| sse/ | | tools, workflow, |
| events/ | | contracts, API |
| automation/ | | router, frontend, |
| config.py, uow.py | | and DB models. |
| | | |
| | | See docs/vr/ for |
| | | the VR engine + |
| | | MCP architecture. |
| | | |
+----+-------------+--+ +---------+----------+
| | |
+----v---+ +-----v------+ +-------v---------+
| Redis | | PostgreSQL | | Per-module ARQ |
| ARQ | | SQLModel + | | queue tracks: |
| queues | | Alembic + | | default, vr, |
| | | pgvector | | vulnerability, |
+--------+ +------------+ | forensics |
src/aila/storage/ | |
+-----------------+
Layer responsibilities
- Platform (
src/aila/platform/) -- routing, runtime construction, shared services, module/tool contracts, LLM client and pipelines, ARQ task registration, workflow engine, SSE bus. Never imports from a feature module. - Modules (
src/aila/modules/) -- domain logic. Each module is a self-contained package implementingModuleProtocol. One module never imports from another. Layout is fixed bydocs/MODULE_STANDARD.md. Current modules:vulnerability(CVE/CWE scanning + intel),forensics(DFIR investigations),vr(vulnerability research -- graph-aware audit, fuzz campaign proposals, enterprise PDF reports, exploit/PoC writer agent),malware(sample-centric reverse engineering over ida-headless-mcp-exp), and thehello_worldreference module. - API (
src/aila/api/) -- FastAPI application (aila.api.app:app). Modules contribute additional routers viaapi_router.py. - Frontend (
frontend/) -- top-level Vite + React + TypeScript shell. Module UIs live undersrc/aila/modules/<id>/frontend/and are mounted by the shell through the frontend module spec. Managed as a pnpm workspace at the repo root. - Storage (
src/aila/storage/) -- SQLModel models, Alembic migrations, config registry, secret store. Vector search uses pgvector with 384-dimensional embeddings. - Task queue -- ARQ on Redis, with per-module queue tracks (default, vulnerability, forensics, vr) so long-running jobs don't starve each other.
For deeper detail see docs/ARCHITECTURE.md.
Every reasoning module in AILA (vr, malware, anything future that
holds a multi-turn LLM conversation against an MCP backend) runs the
same four-phase loop driven by the platform's reasoning engine
(src/aila/platform/services/reasoning.py). Personas differ, tool
surfaces differ, terminal outcome kinds differ -- the loop does not.
+----------------------------------+
| case_state.observables |
| prior tool readings + directives|
| + agent scratchpad (capped) |
+-----------------+----------------+
|
v
+---------+ +------+-------+ +--------+
| THINK |-------->| HYPOTHESIZE |------->| ACT |
| persona | reads | produce or | emits | tool_ |
| prompt | state | refine list | one | run |
| + state | | with claim, | action| OR |
| | | why_plaus., | | submit |
| | | kill_crit. | | OR text|
+---------+ +--------------+ +----+---+
^ |
| |
+-------------- OBSERVE ---------------------+
tool result -> observables_delta
merged into case_state
Think. Each turn, one persona (researcher / critic / implementer)
receives the system prompt + persona prompt + the current
case_state projection. The case_state carries every prior tool
reading the platform decided was worth keeping in attention (tool
observables capped at the last 80, agent scratchpad capped at 15),
plus directives the auto-steering pipeline injected.
Hypothesize. The persona's output is a structured envelope
carrying a hypothesis list. Each hypothesis is a triple of
{claim, why_plausible, kill_criterion} -- the kill_criterion is the
specific evidence that would refute it. Hypotheses persist across
turns and accumulate state (live / rejected / resolved); the
rejection reason and resolution note travel with them. The frontend
renders the live + rejected + resolved aggregate per investigation
so the operator sees the deliberation drift in real time.
Act. The same envelope emits exactly one action: a
tool_run (dispatch one MCP tool call), a submit of a terminal
outcome (AnalysisReport, FindingDraft, TriageVerdict, ...), or a
text-only deliberation turn that updates hypotheses without
external action. The executor dispatches tool_runs through the
module's MCP bridge layer (which adds defenses: auto-poll on
pending, kwarg coercion, alias maps, address-format coercion --
see VR / Malware sections below).
Observe. The tool response flows through a per-adapter pass
that shapes the response into an observables_delta (key-value
pairs the next turn will see) plus a durable observation row in
the module's observation table (cross-investigation memory, kind +
polarity tagged). The case_state merges the delta; the renderer
decides which observables make it back into the next prompt under
the attention cap.
Multi-persona adversarial discipline. The reasoning module's
branch manager spawns multiple personas in parallel against the
same investigation. Each branch holds its own case_state. The
platform's deliberation broker surfaces rejection signal across
branches (a hypothesis rejected by 2+ siblings becomes a
sibling_consensus_rejection directive on the still-live branch).
Claim verifier promotes evidence reaching a confidence floor;
synth assembles the final outcome from the converged claims.
The pattern is what lets two very different modules (source-level vulnerability hunting vs binary malware reverse engineering) share the same engine + the same operator UI + the same observation memory abstraction. Adding a third reasoning module is mostly writing prompts + an MCP bridge + an outcome contract; the loop is already there.
The vulnerability research module (vr) drives a multi-MCP backend with
graph-aware code intelligence, semantic search, and binary analysis. Three
MCP servers run alongside AILA, exposed over HTTP and orchestrated by the
platform's task queue.
+-------------------------------------------------------------+
| AILA backend (Python + ARQ) |
| agent loop -> tool_executor -> bridge tools -> MCP servers |
+----+--------------------+--------------------+--------------+
| | |
+----v-----+ +-------v--------+ +------v---------+
| audit- | | ida-headless- | | semble |
| mcp | | mcp | | (embedded in |
| 18822 | | 18821 | | audit-mcp) |
+----------+ +----------------+ +----------------+
trailmark Hex-Rays + miasm Model2Vec + BM25
graph engine binary engine chunk retrieval
+ GPU CSR + 81 tools
+ 58 tools
- Tool surface: 58 tools over HTTP (
/toolsfor catalog,/tools/<name>for invocation) - Graph engine: trailmark builds a call graph + symbol table on
index_codebase. Per-index cached on disk viaDurableIndexStore, recovered automatically on restart. - GPU acceleration:
from_trailmark()constructs a GPU CSR adjacency matrix when CUDA is present; powersattack_surface,fuzzing_targets,unreachable_from_entrypointson monorepo-scale graphs. - Semantic search via semble:
- Hybrid Model2Vec (potion-code-16M) embeddings + BM25 + RRF + code-aware reranker
- Per-index lazy build in a separate Python process so the parent's GIL stays free during cold builds
- Pickled to
~/.audit-mcp/semble-cache/<index_id>.pklafter first build -- subsequent restarts load in ~9s instead of rebuilding - Tools:
semantic_search(query, top_k, alpha, rerank, filter_*),find_related(file, line, top_k),semble_stats(index_id)
- Read-function fast path:
read_functionqueries the semble chunk index first (matches by name + definition pattern); falls back to a process-cachedTypeResolverinstead of rebuilding it per call (was the source of 15-minute hangs on firefox-scale). - Multi-worker support:
AUDIT_MCP_WORKERSenv /--workersCLI flag. Each worker holds its own engine + semble + TypeResolver caches; AILA's bridge pre-warms all workers on the first call to a newindex_id(Linux/macOS only -- Windows uvicorn multi-worker is broken).
- Tool surface: 81 tools over HTTP (
/toolscatalog) - Engines: Hex-Rays decompiler + miasm IR for control-flow obfuscation, CFF deflattening, symbolic execution, CAPA behavioral rule scanning
- Mutations: Renames, comments, prototypes, and assembly patches are queued through
ida_headless/poll_mutationso concurrent operator + agent edits don't race - Specialised tools: GPU-backed call graph traversal, opaque-predicate proving via SMT, structural binary diffing, exploitability assessment (
assess_exploitability,prove_overflow,prove_bounds_sufficient)
The VR module runs adversarial 3-persona deliberation (researcher / critic / synthesizer) over the MCP tool surface. Each tool call goes through AuditMcpBridgeTool (or its IDA equivalent) which provides:
- Schema-driven kwarg validation -- catches LLM-hallucinated parameters (e.g.
fuzzing_targets(threshold=...)) before the HTTP round-trip and returns a structured "did you mean" error so the next turn self-corrects - Per-action kwarg synonyms -- transparently rewrites common aliases (
top_n->limit,cutoff->min_complexity, etc.) per tool's actual signature - Circuit breaker -- counts repeated failures by both
(server, tool, args)AND(server, tool, error_class)so the agent can't burn turns varying the value of a bad kwarg name; injects a hard pivot directive after 3 consecutive failures - Survey-streak pivot -- after 3 consecutive survey-tool calls (
attack_surface,complexity_hotspots,fuzzing_targets, ...) without a source read, forces the agent intoread_function/taint_paths_to/callers_ofor a finding submission - Language-aware tool suppression -- hides
dead_codeandunreachable_from_entrypointsfrom agents running against C++/Java/Kotlin/C#/Swift/Objective-C/Scala targets (static call graphs are blind to vtable + template dispatch on those languages) - Pending/poll pattern -- heavy operations like
fuzzing_targetson firefox returnstatus=pending + task_id; the bridge pollspoll_taskfor up to ~15 min so AILA's 900s HTTP timeout doesn't kill long graph queries - Lazy pre-warm fan-out -- first call to a new
index_idfires 16 parallelsummary+semble_statsrequests so every uvicorn worker warms its caches before the agent's real query lands
Target ingestion is split into three independently-tracked stages with per-stage status, attempts counter, and reaper:
INGESTION-- audit_mcpindex_codebaseclone + parse (timeout 14400s)CAPABILITY_PROFILE-- D-51 capability rule evaluation (timeout 1800s)FUNCTION_RANKING--fuzzing_targetsranking with GPU CSR (timeout 1800s)
Operator can resume a stuck target via POST /vr/targets/{id}/resume-analysis; the endpoint fans out per non-DONE stage. Reaper runs every minute via ARQ cron, flips RUNNING stages past their timeout to FAILED with "reaper: RUNNING for Xs > Ys timeout".
For day-to-day MCP operations and the full VR agent design see docs/vr/.
The malware module ports the VR adversarial-deliberation pattern
onto sample-centric reverse engineering. Same multi-persona loop
(halvar / noor researchers, maddie / yuki critics, renzo / wei
implementers), same SSE-streamed timeline, same observation memory --
but every tool dispatch is constrained to ida-headless-mcp-exp
only. audit_mcp and android_mcp are explicitly hidden from the
agent-facing catalog and rejected at the executor's allowlist
(backend services like the claim verifier still construct their own
bridge instances).
sample upload
|
v
target_analysis stages (Alembic-tracked, per-stage durable)
open_binary -> auto_analysis -> string_classification
|
v
investigation kinds:
triage | full_analysis | unpack_only | config_extract
| yara_generate | family_attribute
|
v
multi-persona reasoning loop:
investigation_setup -> investigation_loop -> investigation_emit
|
v
outcomes:
ANALYSIS_REPORT | TRIAGE_VERDICT | UNPACK_ARTIFACT
| CONFIG_BLOB | YARA_RULE_DRAFT | FAMILY_ATTRIBUTION
| FINDING | OUTCOME_REVIEW
The agent dispatches exclusively against ida-headless-mcp-exp
running on port 18821. The bridge layer
(platform/mcp/bridges/ida_headless.py) adds nine defenses on top
of the raw catalog so the agent's habits don't cost turns:
- Auto-poll on
status: pending-- bridge sleeps + re-POSTs the same call (2s -> 3s -> 4.5s -> 8s capped) until the per-call async work lands ready or the 240s budget runs out.poll_analysisitself is the only excluded action. - Dead-arbiter fail-fast -- when the ida-headless response shape
matches the dead-worker signature (
status=pending+worker_phasein{exiting_idle, crashed, ""}+heartbeat_age_s >= 600), the bridge skips the 240s poll and returns a structureddead_worker_diagnosticerror naming the SHA, heartbeat age, queue depth, and the exact operator action (restart ida-headless, clearcrash_counts.json, re-upload to force fresh analysis). Threshold override:IDA_HEADLESS_DEAD_WORKER_HEARTBEAT_S. - Per-call dedup -- identical
(action, sorted_kwargs)withinIDA_HEADLESS_DEDUP_TTL_S(default 300s) replays the cached ready payload. Scoped to 23 read-only actions (xrefs_to/from, decompile, find_api_call_sites, callers_of, build_call_tree, list_strings/functions, capa_scan, detect_crypto_primitives, etc.). State mutators (open_binary, upload, patch_assemble) and freshness-sensitive (poll_analysis) are excluded. Cached hits stamp_ida_bridge_dedup: hitso the executor can distinguish replay from fresh. Per-call bypass: pass_ida_bridge_no_dedup=True. - IDA auto-name -> hex coercion -- 17 address-shaped kwargs across
the 81-tool catalog rewrite
sub_<hex>/loc_<hex>/unk_<hex>to0x<hex>before dispatch.avoid_addresses(list) gets per-element rewriting. encodingvalue alias -- the string-family tools (list_strings,get_string_at) acceptutf16/utf-16/utf-16le/utf16-leas aliases for the canonicalutf16le. Closes the round-trip with the same-named label the server emits underby_encoding; an agent passing back the value it just read fromcount_onlyno longer falls into atotal=0false negative.- Pagination-noise drop --
offset/limit/cursor/top_kget silently stripped when the target tool doesn't declare them, so snapshot tools no longer TypeError on agent habits. search_patternalias map -- rewritespattern/pattern_str/queryto the canonicalpattern_typeenum kwarg.status=Nonepayload normalization -- tools likebinary_metadatathat omit a top-levelstatusfield getstatus='ready'injected so the downstream executor's whitelist doesn't synthesize a spurious empty error._ALWAYS_SUPPRESSenforcement -- a curated set of tools the agent must never reach is checked BEFORE the specialized-adapter lookup AND subtracted from_effective_tools(which would otherwise re-introduce them via the runtime bridge catalog). Current suppress set:ida_headless.classify_strings(regex categorizer that returns 19 buckets on a 10,254-string binary; agents derail by treating its empty output as evidence) andaudit_mcp.search_source(deprecated). Suppressed tools also drop fromregistered_tools()+specialized_tools()so the diagnostic listings stay consistent.
The XREF adapter additionally surfaces a pagination_hint block
on payload + a one-line directive in the observable when
total > MAX_LIST_PREVIEW=20. The directive names the suppressed
row count + the exact follow-up call shape
(offset=20, limit=20) + the call_id for direct payload
access. The full xref array is preserved verbatim in payload.xrefs
regardless of length; only the per-turn observable preview is trimmed.
The system prompt teaches a four-stage extraction workflow instead of pattern-matching on hostname-shaped substrings:
- Locate the config loader -- find via
find_api_call_sites('FindResourceW')for embedded-resource patterns, decryptor xrefs for crypto blobs, orRegQueryValueExWfor registry-stored configs. - Recover the storage layout -- decompile the loader, read off exact resource ID / data offset, decryption algorithm + key + IV, serialization format, field offsets.
- Apply the layout --
read_memory(VA, size)the encrypted blob, decrypt with binary-sourced constants, parse with the known offsets, identify the C2 field. - Cross-validate -- walk from loader-populated global to its
consumer at
WinHttpConnect/InternetOpenUrlA/connect. If the path doesn't close, it isn't the C2 -- keep going.
ida-headless-mcp-exp now eager-initializes _Frontend in main()
/ main_http() before binding the transport, so recover_all()
populates _binaries from cache/<sha>/state.json BEFORE the
server accepts the first HTTP request. AILA workers carrying stale
binary_id values in mcp_handles_json survive restarts without
seeing Unknown binary_id. Defensive _sha fallback handles
out-of-band registrations (e.g. a stdio MCP transport writing
state.json that the HTTP server's already-completed sweep missed).
- Reset -- toolbar button on the investigation page. Server
endpoint at
POST /malware/investigations/{id}/resetwipes messages + observations + outcomes + forked branches +workflow_state_cursorarchive in one transaction, flips status back toCREATED. Refuses whileRUNNING(operator must pause first);PAUSEDis fine because the cursor wipe handles the archive that/resumewould otherwise rely on. - Force Re-enqueue -- visible when status is
RUNNINGand the reaper missed a stall (engine reports in-flight but no task heartbeat). Calls the same/re-enqueueendpoint the reaper would. - Re-synthesize -- "Synthesize again" button on the investigation
page (the
Synthesis & Narrativecard under the primary outcome).POST /malware/investigations/{id}/synthesizeaccepts{ force, tone, length, enumerate_every_suspicious, operator_focus }. Defaultforce=True; tonesoperator | executive | technical | analyst | forensic; lengthsbrief | standard | exhaustive.enumerate_every_suspicious=Trueis the "don't drop anything" mode that walks every persona's answer + reasoning and surfaces every distinct suspicious item (string, address, function, IOC, persistence artifact, decoded blob). Runs against an already-COMPLETEDinvestigation without flipping its status; the structuredpanel_summaryis overwritten plus every promoted field (family_attribution,capabilities,iocs,attribution_rationale,headline_verdict,detection_guidance,next_actions,panel_dissent,inconclusive_areas,inconclusive_capabilities) is re-derived. - Generate narrative -- separate artifact from synthesis. A
long-form chronological writeup stored under
payload.investigation_narrativeon the canonical outcome, alongside (not replacing)panel_summary. Endpoint:POST /malware/investigations/{id}/narrativewith{ force, tone, length, operator_focus }. Tonesblog | incident_report | thriller | academic | casual; lengthsshort(~1.5-2.5K words),standard(~3.5-5.5K words),long(~8-15K words). Schema enforcesbody.min_length=4000so the LLM cannot bail with a 200-char intro stub. UI button opens the rendered markdown in a side modal with table of contents, copy-to-clipboard, and the title / tone / length / word count metadata strip. - Direct outcome edit -- agents and operators alike can patch a
draft outcome's payload via the
edit_outcomeaction. Counterpart to the deferredrequest_editvote (which only suggests edits the synthesis agent picks up on the next pass);edit_outcomemerges patches immediately. Refused on non-draft outcomes; protected workflow-owned keys (panel_contributions,panel_summary,verifier_report,applied_by_synthesis) are dropped from the merge. Every applied edit writes an audit row. - Veto threshold = 2 (chorus, not solo) -- a single sibling
rejectvote no longer kills an outcome. A second sibling must concur (VETO_K=2inservices/outcome_review.py). Single rejects still record on the outcome and surface in the proposing branch's prompt so it can react; the state flip waits on the chorus. Approve quorum (approve_count >= quorum_k) still ships the outcome; veto is evaluated BEFORE approve so a tied chorus resolves to rejected. - Observations debug panel -- operator-facing list of every observation row recorded under the investigation, with polarity and kind filter chips. Surfaces what evidence the agent has actually committed to durable storage.
- Hypothesis aggregate -- per-investigation projection of live / rejected / resolved hypotheses across every branch.
Prerequisites
Required:
- Python 3.11+
- Node.js 20+ (
corepack enableto activate pnpm) - PostgreSQL 15+ with
pgvector - Redis 7+
- An OpenAI-compatible LLM endpoint (OpenAI, Anthropic via LiteLLM, Ollama, vLLM, ...)
Optional MCP servers (only if you want the vr or malware modules):
- audit-mcp on
:18822--vrsource-repo targets - ida-headless-mcp-exp on
:18821--malware, andvron binary targets - android-mcp on
:18823--vronandroid_apktargets
The platform, vulnerability, forensics, and hello_world modules run without any MCP.
Steps
-
Clone and install.
git clone https://github.com/project-lambda-zero/AILA.git cd AILA make install # pip install -e ".[dev]" + corepack enable && pnpm install
-
Configure environment.
cp .env.example .env
Set at minimum
AILA_DATABASE_URL,AILA_PLATFORM_REDIS_URL,AILA_JWT_SECRET_KEY(openssl rand -hex 32),AILA_ADMIN_PASSWORD(first-boot bootstrap, remove after), and theAILA_PLATFORM_LLM_*group. If you plan to use MCPs, uncomment theAUDIT_MCP_URL/IDA_HEADLESS_URL/ANDROID_MCP_URLblock in.env.example. Full reference: docs/ENV_VARS.md. -
Bring up Postgres + Redis and initialize the schema.
make dev-up make db-init # first run only: create tables + stamp Alembic head # subsequent runs: # make migrate
-
Start MCP servers (skip if not using
vrormalware). Each in its own terminal:audit-mcp --mode http --port 18822 # vr ida-headless-http --port 18821 # malware, and vr on binary targets python -m android_mcp --mode http --port 18823 # vr on android_apk
See docs/QUICKSTART.md section 4 for clone + install of each MCP.
-
Start AILA in three terminals.
make backend # REST API on :8000 make frontend # Vite dev server on :3000 (single SPA, all module UIs) make worker # default ARQ queue
Per-module workers when the corresponding module is in play:
make worker-vr # vulnerability research make worker-vuln # vulnerability scans make worker-forensics # DFIR investigations make worker-malware # malware reverse engineering
For the full walkthrough (admin user creation, MCP install details, smoke tests, common pitfalls), see docs/QUICKSTART.md.
| module_id | Description | Status |
|---|---|---|
vulnerability |
SSH package inventory, distro-aware advisory resolution, CVE enrichment, scoring, and reporting. | production |
forensics |
Remote forensic evidence triage over SSH: disk images, memory dumps, PCAPs, write-up generation. | production |
vr |
Vulnerability research: graph-aware source/binary audit (audit-mcp + IDA Headless MCP), hypothesis-driven reasoning, fuzz campaign proposals (audit\u2192fuzz pipeline), enterprise PDF reports with LLM writer agent, automatic exploit/PoC drafting, variant hunting with child-investigation spawning. | production |
malware |
Malware reverse engineering: VR-pattern multi-persona deliberation over ida-headless-mcp-exp only. Six investigation kinds (triage / full_analysis / unpack_only / config_extract / yara_generate / family_attribute), deterministic four-stage C2 config extraction with mandatory string-sweep + xref-chain follow-up, two-stage C2 hunt (Stage 1 loader URLs vs Stage 2 dropped-payload endpoints), fan-out sub-investigation spawning, observation memory with base64 / hex auto-decode, operator controls (reset / re-enqueue / re-synthesize with tone+length / generate-narrative writeup / direct edit_outcome patches), chorus-veto outcome review (VETO_K=2), structured synthesis promoting family / capabilities / IOCs / detection guidance / next actions onto the canonical payload. |
production |
hello_world |
Minimal reference module proving the ModuleProtocol contract end-to-end. |
example |
Modules are auto-discovered at platform boot by scanning src/aila/modules/*.
Packages whose name starts with _ are skipped (used for templates and
fixtures). To add a new module, follow docs/MODULE_STANDARD.md
and the worked tutorial in docs/MODULE_TUTORIAL.md.
Common targets in the root Makefile:
| Target | What it runs |
|---|---|
make install |
pip install -e ".[dev]" plus corepack enable && pnpm install |
make dev-up |
docker compose -f infra/utilities/docker-compose.yml up -d postgres redis (idempotent) |
make dev-down |
Stop dev infra containers (keeps data volumes) |
make dev-reset |
Stop containers and wipe data volumes |
make dev-logs |
Follow compose service logs |
make dev-status |
docker compose ps |
make db-init |
python scripts/db_init.py -- create tables + stamp Alembic head (first run only) |
make migrate |
cd src/aila && alembic upgrade head |
make dev |
Print the canonical dev workflow (no services started) |
make backend |
Ensure dev-up + db-init, free port 8000, run uvicorn aila.api.app:app --host 0.0.0.0 --port 8000 --reload |
make frontend |
Free port 3000, run pnpm --filter @aila/shell run dev (Vite on :3000) |
make frontend-build |
pnpm --filter @aila/shell run build (production SPA bundle) |
make storybook |
pnpm --filter @aila/shell run storybook |
make worker |
python -m aila worker (default queue) |
make worker-vr |
python -m aila worker -q vr |
make worker-vuln |
python -m aila worker -q vulnerability |
make worker-forensics |
python -m aila worker -q forensics |
make dev-all |
Bring up all services in one terminal (Ctrl+C stops everything) |
bash start.sh |
Spawn audit-mcp + backend + 4 workers + frontend in one shot (Windows: Git Bash + PowerShell) |
docker compose -f infra/utilities/docker-compose.full.yml up --build |
Full-stack containers: postgres + redis + api + 4 workers + frontend. See docs/DEPLOYMENT.md. |
make test |
pytest, excluding tests/test_e2e*.py |
make test-e2e |
pytest tests/test_e2e.py -v (requires live infrastructure) |
make test-frontend |
pnpm --filter @aila/shell run test (shell package only; module frontends use pnpm -r run test) |
make lint |
ruff check src/aila/ |
make typecheck |
pnpm -r run type-check (every workspace package, shell + modules) |
make honesty |
python -m aila.tools.honesty_audit src/aila --whitelist honesty_whitelist.py |
make compile |
python -m compileall -q src/aila |
make build |
pnpm --filter @aila/shell run build (production SPA bundle) |
make check |
lint + honesty + compile + typecheck (the full pre-PR gate) |
make security-scan |
pip-audit --strict --desc and bandit -r src/aila -q -ll |
make clean |
Remove __pycache__/ directories and coverage artifacts |
Run make check before opening a PR. Contributor workflow, branch policy,
review expectations, and the honesty audit rules are documented in
docs/CONTRIBUTING.md.
The aila entry point (aila = "aila.cli:app") is a Typer application.
Invoke aila --help to list every subcommand and command group; the most
common entry points are summarised below.
| Command | Purpose |
|---|---|
aila serve |
Start the FastAPI REST API via uvicorn |
aila worker [-q <queue>] |
Start an ARQ worker for the given queue track (default: default) |
aila task "<question>" |
Ask a natural-language question routed through the platform agent |
aila analyze [--target <name>] |
Run a vulnerability scan across registered targets (or one) |
aila add-ssh ... |
Register an SSH-reachable system for the vulnerability module |
aila create-api-key |
Mint an admin-role API key for first-boot bootstrap |
aila health |
Probe platform and provider readiness |
Command groups expose related subcommands:
aila config (runtime config registry),
aila tool (invoke registered platform tools directly),
aila cache (manage decision and intel caches),
aila policy (scoring policy management),
aila feedback (operator knowledge entries),
aila report (PDF and CSV reporting),
aila schedule (scheduled scans),
aila intel, aila ops, aila auto, aila digest
(fleet intelligence, operational metrics, automation, executive digests).
- Base URL (dev):
http://localhost:8000 - OpenAPI / Swagger UI:
http://localhost:8000/docs - OpenAPI JSON:
http://localhost:8000/openapi.json - Authentication:
POST /auth/loginwith{"username", "password"}returns a JWT (data.access_token) used asAuthorization: Bearer <token>for all subsequent calls;POST /auth/tokenexchanges an API key for the same envelope. RBAC roles areadmin,operator,reader-- see docs/SECURITY_MODEL.md. - Streaming: long-running scans, sessions, and tasks expose SSE endpoints
(e.g.
/scans/{id}/events,/tasks/{id}/events). Integration patterns are documented in docs/SSE_GUIDE.md. - Errors: structured error envelope catalogued in docs/API_ERRORS.md.
The OpenAPI document is the source of truth for the route surface; the
/docs UI lists every endpoint, request schema, and response schema.
| Document | Covers |
|---|---|
| docs/ARCHITECTURE.md | System diagram, layer responsibilities, data flow, runtime constraints |
| docs/PLATFORM_INTERNALS.md | X-ray: full request lifecycle traced through every platform layer |
| docs/QUICKSTART.md | Expanded onboarding walkthrough with troubleshooting |
| docs/CONTRIBUTING.md | Contributor workflow, branch policy, review expectations |
| docs/MODULE_STANDARD.md | Required module layout, contracts, and lifecycle (v2.1) |
| docs/MODULE_TUTORIAL.md | Step-by-step authoring of a new module |
| docs/MODULE_AGENT_GUIDE.md | Module context conventions for LLM-driven flows |
| docs/FRONTEND_MODULE_STANDARD.md | Frontend shell and per-module UI contribution contract |
| docs/forensics/ | Forensics module domain reference and design history |
| docs/DB_SCHEMA.md | Database tables, relationships, and ownership |
| docs/DATABASE_MIGRATIONS.md | Alembic policy, conventions, and migration authoring |
| docs/CONFIG_REGISTRY.md | Config resolution chain (env -> registry -> defaults) |
| docs/ENV_VARS.md | Environment variable reference |
| docs/SECURITY_MODEL.md | Auth, RBAC, API keys, JWT lifecycle |
| docs/DATA_PROTECTION.md | Data posture modes, LLM redaction, input/output sanitization |
| docs/API_ERRORS.md | API error catalog |
| docs/OPENAPI_NOTES.md | OpenAPI generation notes and conventions |
| docs/SSE_GUIDE.md | Server-sent events: usage, reconnection, curl examples |
| docs/TASK_QUEUE_OPS.md | ARQ worker operations, queue tracks, retry semantics |
| docs/LLM_INTEGRATION.md | LLM client, pipelines, model selection, transparency posture |
| docs/WORKFLOW_GUIDE.md | Durable state machine: handler contract, do/don't, production examples |
| docs/DEPLOYMENT.md | Production deployment guide |
| docs/TEST_GUIDE.md | Testing conventions, fixtures, e2e gating |
| docs/GOLDEN_RULES.md | Code quality rules enforced by review and tooling |
| docs/HONESTY_AUDIT.md | Structural honesty rules enforced by aila.tools.honesty_audit |
| docs/PITFALL_GUIDE.md | Common mistakes when working on AILA |
| docs/PRODUCTION_RUBRIC.md | Readiness rubric for shipping a module to production |
| docs/vr/ | VR engine internals: reasoning loop, IDA Headless MCP, exploit automation |
| docs/VR_INSTALLATION_GUIDE.md | Standing up audit-mcp + IDA Headless MCP next to AILA |
| CHANGELOG.md | Version history |
AILA is licensed under the GNU Affero General Public License v3.0. See LICENSE for the full text.