-
Notifications
You must be signed in to change notification settings - Fork 0
Development Guide
How the repository is organised, how to set up an environment, a walkthrough of every package and module, recipes for extending each subsystem, and the coding and commit conventions. Tests are covered in Testing, releases in Releasing.
| Path | Purpose |
|---|---|
aisrf/ |
the Python package (105 modules) |
aisrf/gateway/ |
interception: router, parser (request normalisation), policy engine, forwarder, hold registry, canary, programmatic pipeline |
aisrf/analysis/ |
analyzer framework (base.py, runner.py, custom_rules.py) and the built-in analyzers in analyzers/
|
aisrf/guardrails/ |
LLM Guard, NeMo Guardrails, Lakera Guard and Rebuff integrations, plus the bundled offline NeMo config in nemo_default/
|
aisrf/tickets/, aisrf/agents/, aisrf/audit/
|
ticket lifecycle and SSE streams, agent registry and scan tokens, hash-chained audit log |
aisrf/redteam/ |
corpus loader, YAML corpus (corpus/*.yaml), mutators, evaluators, campaign engine, comparison groups, REST API |
aisrf/scanners/ |
garak, promptfoo, PyRIT and PyRIT-Ship engines, the scan runner and REST API |
aisrf/codereview/ |
source code review: intake, inventory, engines (engines/), rule packs (rules/), semgrep rules (rules/semgrep/*.yaml), SARIF, REST API |
aisrf/reports/ |
format-agnostic report model, builders, renderers, REST API |
aisrf/dashboard/ |
server-rendered reviewer UI: router.py, Jinja templates in templates/, page scripts and CSS in static/
|
aisrf/integrations/ |
client-side helpers: python_sdk.py (httpx and requests patching, AISRFClient), mitm_addon.py
|
aisrf/cli.py, aisrf/mcp_server.py, aisrf/desktop.py
|
Typer CLI, MCP server (HTTP and stdio), desktop mode and app directory |
aisrf/config.py, aisrf/settings_store.py, aisrf/settings_router.py
|
pydantic settings, runtime overrides, Settings API |
aisrf/models.py, aisrf/db.py, aisrf/security.py, aisrf/auth.py, aisrf/logging.py, aisrf/metrics.py, aisrf/notifications.py, aisrf/taxonomy.py, aisrf/main.py
|
ORM, engine, crypto, reviewer auth, structured logging, Prometheus registry, webhooks, OWASP taxonomy, app factory |
sdk/node/ |
the aisrf-intercept npm package (index.js, index.d.ts, test.js) |
examples/ |
runnable clients: OpenAI sync and async, Anthropic, LangChain, tool-using agent loop, curl, MCP client config |
tests/ |
pytest suite, asyncio_mode = auto
|
docs/ |
Markdown documentation and images; wiki/ is synced to the GitHub wiki by .github/workflows/wiki-sync.yml
|
deploy/ |
systemd, launchd, Windows service, Kubernetes manifests, Helm chart |
packaging/pyinstaller/ |
aisrf.spec, launcher.py, manifest.py for the binaries |
scripts/ |
install.sh, install.ps1, build_binary.py, bump_version.py, check_style.py, changelog_notes.py, brand_assets.py, screenshots.py
|
Dockerfile, docker-compose.yml, Makefile, .env.example, pyproject.toml
|
build and configuration entry points |
The project convention is a single virtualenv at .venv, created and populated with uv; never use another interpreter path.
git clone https://github.com/keyuraghao/aisrf.git && cd aisrf
uv venv .venv --python 3.12
uv pip install --python .venv/bin/python -e ".[dev,postgres,mitm]" # what `make dev` runs
uv pip install --python .venv/bin/python -e ".[dev,all]" # every engine (large)
cp .env.example .env # edit AISRF_SECRET_KEY, AISRF_ADMIN_PASSWORD, AISRF_ADMIN_API_TOKEN
.venv/bin/aisrf init-db
.venv/bin/aisrf serveMakefile targets: make venv, make install, make dev, make run (PORT=8080), make test, make lint, make style, make fmt, make bump VERSION=x.y.z [DRY=1], make node-test, make docker-build, make docker-run, make compose-up [PROFILE=postgres], make compose-down, make clean. Node 18+ is only needed for sdk/node (node sdk/node/test.js).
.venv/bin/aisrf serve --host 127.0.0.1 --port 8080 --reload--reload passes uvicorn's reloader for aisrf.main:create_app (factory mode); workers are forced to 1 when reloading. Templates are re-read by Jinja and static files are served from disk, so dashboard edits appear on refresh; Python edits restart the process (in-flight synchronous waits are dropped, tickets stay PENDING). AISRF_LOG_LEVEL=DEBUG and AISRF_DB_ECHO=true help while developing. watchfiles is silenced to WARNING by the logging setup.
Every request follows aisrf/gateway/router.py::intercept: extract the agent key, authenticate, enforce rate limit and body size, parser.normalize(), analysis.runner.analyze_request(), policy.evaluate(), tickets.service.create_ticket() and the hold, then forward_ticket() with forwarder, response analysis and _finish(). Modules by package:
-
aisrf/__init__.py:__version__. -
main.py:create_app()factory,CorrelationMiddleware(bindsrequest_id,path,methodin structlog contextvars, echoesX-Request-ID),lifespan()(logging,init_db,ensure_admin,settings_store.load_overrides, shared httpx client, internal token, sweeper task, notifier, runner shutdowns),_background_sweeper(),/healthz,/readyz,/metrics, router registration order (gateway catch-all last). -
config.py:Settings(BaseSettings)with env prefixAISRF_and.envsupport,fernet_key,is_production,ensure_dirs(), cachedget_settings(),reset_settings_cache(). -
settings_store.py:LOCKEDfields,GROUPSfor the Settings page,NAMESPACE_DEFAULTS(analyzers,rules,integrations,ui,policy),schema(),coerce(),load_overrides(),update_core(),reset_core(),get_namespace(),get_value(),update_namespace(),reset_namespace(),export_all(),import_all(). -
settings_router.py:/api/settingsschema, analyzers, guardrails status and health, taxonomy, core update and reset, namespace get, update and reset, export and import. -
models.py:utcnow,new_id, enumsTicketStatus,RiskLevel,CampaignStatus,CodeReviewStatus; tablesReviewer,Agent,ScanToken,Ticket,TicketEvent,AgentEvent,AuditEntry,Campaign,ProbeResult,AppSetting,Counter,CodeReviewRun,CodeReviewFinding. -
db.py:Base,get_engine()(SQLitetimeout=30),get_sessionmaker(),session_scope(),get_session()dependency,init_db()(WAL, foreign keys,create_all, counter seeding),dispose_db(). -
security.py:hash_passwordandverify_password(PBKDF2),generate_api_key,hash_api_key,api_key_prefix, session token create and read (itsdangerous),encrypt_secretanddecrypt_secret(Fernet),redact_headers. -
auth.py:Principal,ensure_admin,optional_principal,current_principal,require_role,/api/auth/login,/logout,/me,/reviewersCRUD and password endpoints. -
logging.py:configure_logging()(console,aisrf.jsonl, per-agent files),get_logger(),agent_file_logger(),EventBroadcaster(SSE pub-sub with replay),json_dumps. -
metrics.py:Metrics.inc(),observe(),render()in Prometheus text. -
notifications.py:Notifierqueue, Slack Block Kit payloads forhooks.slack.com, raw JSON elsewhere. -
taxonomy.py:OWASP_LLM_TOP10,GRESHAKE_THREATS,GRESHAKE_DELIVERY,THACKER_TECHNIQUES,CATEGORY_MAP,classify(),owasp_ids(),owasp_label(),enrich(),coverage(),catalogue(). -
cli.py: Typer app with sub-appsagent,tickets,redteam,audit,codereview; local commands use_run_db, remote ones theRemoteREST client (--url,--token). -
mcp_server.py:MCPServerimport shim for mcp 2.x and 1.x,ApiClient,build_server()with 44@server.tool()functions plus resources and thereview_ticketprompt,BearerGuardASGI wrapper,mount_mcp()at/mcp,SessionManagerRunner,run_stdio(). -
desktop.py: see Desktop-Mode.
-
router.py:_extract_key,_authenticate,_client_ip,intercept,forward_ticket,_iter_upstream,_finish, routesopenai_compatible(/v1/{path}),generic_proxy(/proxy/{path}),poll_ticket(/gateway/tickets/{id}). -
parser.py:detect_endpoint,normalize(OpenAI chat, completions, responses, embeddings, Anthropic messages, Gemini, Ollama, Cohere, generic JSON),extract_response_text. -
policy.py:PolicyDecision,evaluate(active flag, allowed paths and models, deny patterns, thresholds, mandatory review for critical secrets or exfiltration, global policy namespace). -
forwarder.py:build_upstream_url,build_upstream_headers(credential swap),filter_response_headers,make_client,build_request,summarize_error,error_json. -
hold.py:HoldRegistry(asyncio.Event fast path, DB polling slow path). -
canary.py:new_canary,inject,leaked. -
pipeline.py:submit()andSubmitResult, the programmatic path used by the red-team and scanner engines.
-
base.py:Severity,Finding(analyzer, category, severity, title, description, evidence, location, confidence, tags, metadata;to_dict()enriches with taxonomy),AnalysisResult,Analyzerprotocol,level_for_score,score_findings. -
runner.py:register(analyzer, response=False),list_analyzers,_load_builtin(importsanalyzers, custom rules andguardrails),_activeand_apply_overrides(Settings > Analyzers),analyze_request,analyze_response; every analyzer runs concurrently and isolated. -
custom_rules.py:CustomRulesRequest,CustomRulesResponseover therules.customnamespace. -
analyzers/common.py: text normalisation, homoglyph folding, zero-width stripping, leet folding, masking,iter_messages,iter_tools,make_finding,Signature,compile_signatures,MAX_SCAN_CHARS. -
analyzers/:prompt_injection,jailbreak,pii(scan_pii, Luhn, IBAN, national ids),secrets(scan_secrets, entropy),data_exfil,tool_abuse,harmful_content,obfuscation,anomaly,llm_judge(registered only withenable_llm_judge),response_analyzers(system prompt leak, PII leak, secrets leak, refusal, harmful compliance, exfil markers, canary leak).
__init__.py registers rebuff.analyzer, llm_guard.input_analyzer, nemo.input_analyzer, lakera.request_analyzer and the three output analyzers, exposes status() and health_check(). _common.py gives integration_config, is_enabled, library_installed, throttled logging, severity_for_score, text extraction. Each integration module implements analyzer classes plus status() and health_check(); heavy imports happen lazily. rebuff.py reimplements the heuristic layer natively (heuristic_score, detect_injection, canary helpers) with optional SDK and LLM layers.
-
tickets/service.py:create_ticket,set_analysis,apply_policy,decide,bulk_decide,mark_forwarding,mark_completed,mark_failed,mark_expired,expire_stale,purge_old,get_ticket(id or number),list_tickets,list_events,stats,ticket_to_dict. -
tickets/router.py:/api/ticketslist, stats, bulk approve and deny, get, events, approve, deny;/api/auditand/api/audit/verify; SSE/api/stream/tickets,/logs,/campaigns. -
agents/service.py:create_agent,rotate_api_key,update_agent,authenticate_api_key,mint_scan_token,revoke_scan_tokens,check_rate_limit,log_agent_event,list_agent_events,agent_stats. -
agents/router.py:/api/agentsCRUD,rotate-key, disable, events, stats. -
audit/service.py:record,list_entries,count_entries,verify_chain.
corpus.py (Probe pydantic model, Corpus, load_corpus, get_probes), mutators.py (base64_wrap, rot13, leetspeak, reverse_text, payload_split, prefix_suffix_jailbreak, apply_mutator, mutated_id), evaluators.py (evaluate gives VULNERABLE, RESISTED, BLOCKED, ERROR or INCONCLUSIVE), engine.py (CampaignRunner, build_summary, compare_group), service.py, router.py (/api/redteam/corpus, campaigns lifecycle, groups).
base.py (ScanEngine protocol, registry, register, ScanRunner, TicketMatcher, token issue and revoke, run_subprocess), garak.py, promptfoo.py, pyrit.py, pyrit_ship.py, service.py (campaigns, matrix, compare), router.py (/api/scanners/... plus the PyRIT-Ship compatible surface).
config.py (defaults, scanner_binary), intake.py (archives, URLs, git, local paths, snippets), inventory.py (files, languages, manifests, framework detection), engines/ (rules_engine, semgrep_engine, bandit_engine, llm_engine), rules/base.py (Rule, FileContext, Match, matchers lines, near, absence, py, any_of, node_match, factory rule()), six packs, rules/semgrep/*.yaml, findings.py (fingerprint, dedupe, risk score), secrets.py (masking), sarif.py, service.py, engine.py (CodeReviewRunner), report.py, router.py (/api/codereview).
reports/model.py (Report, KeyValueSection, TableSection, TextSection, FindingsSection, ChartSection), builders.py (BUILDERS, KINDS), renderers.py (FORMATS, RENDERERS, render, negotiate), router.py (/api/reports/{kind}). dashboard/router.py renders only the shell (_page with the principal); each page template extends base.html and its script in static/<page>.js loads data from /api/*. integrations/python_sdk.py and mitm_addon.py are client side and must import without optional extras (CI checks this).
- Create
aisrf/analysis/analyzers/<name>.pywith an object exposingname,descriptionandanalyze(normalized, context) -> list[Finding](sync or async). Usecommon.iter_messages,make_finding,snippetand respectMAX_SCAN_CHARS; never raise (the runner isolates exceptions, but keep it pure and bounded). - Register it: add the module to
_REQUEST_ANALYZERSinaisrf/analysis/analyzers/__init__.py, orregister(obj, response=True)for a response analyzer. - Map its
categoryinaisrf/taxonomy.py::CATEGORY_MAPso findings carry OWASP, Greshake and Thacker tags. - Tests in
tests/test_analysis.py: one positive sample and one benign sample (false positives cost reviewer time). Document it indocs/ARCHITECTURE.mdand the Analyzers page.
- Create
aisrf/guardrails/<vendor>.pywith request and response analyzer classes whoseanalyzereadsintegration_config("<vendor>"), returns[]whenis_enabled()is false or the library is missing, imports the vendor SDK lazily, and logs failures withlog_throttled. Providestatus()andhealth_check(). - Add default settings under
NAMESPACE_DEFAULTS["integrations"]["<vendor>"]inaisrf/settings_store.py. - Register in
aisrf/guardrails/__init__.py(REQUEST_ANALYZERS,RESPONSE_ANALYZERS,INTEGRATIONS). - Optional dependency: add it to the
guardrailsandallextras inpyproject.toml(discuss in the PR) and toEXCLUDESinpackaging/pyinstaller/manifest.pyif heavy. - Tests in
tests/test_guardrails.pywithpytest.importorskip.
- Create
aisrf/redteam/corpus/<category>.yamlwithcategory,description, optionaldefaults(severity,expected) andprobes. Each probe needs a uniqueid,technique,name,description,promptormessages,success_indicators(valid regexes), optionalcanary,tags;severityin the allowed set andexpectedinEXPECTED_VALUES. - Map the category and techniques in
aisrf/taxonomy.pyso summaries show OWASP coverage. -
aisrf redteam corpuslists it;tests/test_redteam.pyvalidates the corpus loads.
- Create
aisrf/scanners/<engine>.pyimplementing theScanEngineprotocol:name,description,installed(),capabilities(),list_probes(),plan(options),async run(campaign_id, http). UseScanRunnerhelpers (issue_token,run_subprocess,record_results,finish) so tickets are linked and tokens revoked. - Call
base.register(<Engine>())at import time and import the module fromaisrf/scanners/__init__.py. - Defaults under
NAMESPACE_DEFAULTS["integrations"]["<engine>"]. - Tests in
tests/test_scanners.py, skipped when the tool is absent.
- Pick the pack file in
aisrf/codereview/rules/and append arule(...)to itsRULESlist: idAISRF-<PACK>-NNN,severity,confidence,category,cwe,description,why,remediation,languages,matcherbuilt fromlines(),near(),absence(),py()(AST callback usingrules/pyast.py),any_of(),node_match();engines=("rules",)or("rules", "semgrep"). - For the semgrep counterpart add an entry to
aisrf/codereview/rules/semgrep/<pack>.yamlwithmetadata.aisrf_ruleset to the same id,pack,severity,confidence,cwe,owasp,category; validate withsemgrep --validate --config aisrf/codereview/rules/semgrep/. -
RULES_BY_IDasserts unique ids at import. Add a fixture totests/test_codereview.pyand a row to the rule table indocs/CODE_REVIEW.mdand Code-Review-Rules.
- Implement
render_<fmt>(report: Report) -> bytesinaisrf/reports/renderers.pywalkingreport.sections(KeyValueSection,TableSection,TextSection,FindingsSection,ChartSection). - Add the format to
FORMATS(media type, extension, description),RENDERERS, and aliases inALIASESandMEDIA_TO_FORMAT. - The REST API, CLI (
--format), dashboard and MCP pick it up automatically. Test intests/test_reports.py. Add a report kind by writingbuild_<kind>_reportinbuilders.pyand listing it inBUILDERSandKINDS.
- Template
aisrf/dashboard/templates/<page>.htmlextendingbase.html; scriptaisrf/dashboard/static/<page>.jsfetching/api/...with the helpers fromapp.js. - Route in
aisrf/dashboard/router.py:@router.get("/<page>")calling_page(request, "<page>.html", "<page>", p); add the navigation entry inbase.html. - Any new data needs an
/api/*endpoint guarded bycurrent_principalorrequire_role. - Both directories are already in
PACKAGE_DATA_DIRSand[tool.setuptools.package-data], so nothing to add for packaging.
- MCP: inside
build_server()inaisrf/mcp_server.pyadd an@server.tool()async function with typed parameters and a docstring (the docstring becomes the tool description) that callsapi.call(method, path, params=..., json_body=...). Update the tool count indocs/MCP.mdandtests/test_mcp_cli.py. - CLI: in
aisrf/cli.pyadd@<sub>_app.command("<name>"); remote commands takeurl: UrlOpt = DEFAULT_URL, token: TokenOpt = Noneand useRemote(url, token).json(...); local commands use_run_db(coro). Print withconsoleand the_kv_tableor_rows_tablehelpers. Document in CLI-Reference and the README command list.
- Add a typed field with a default to
Settingsinaisrf/config.py(env name isAISRF_<FIELD>upper-cased). - Add it to a group in
settings_store.GROUPSso it appears on the Settings page (or toLOCKEDif it must only come from the environment);SENSITIVEmasks names matching password, secret, token or key. - Document it in
.env.exampleanddocs/CONFIGURATION.md, and in the Helmvalues.yamlcomment if operators need it. - Read it through
get_settings()(process defaults) orsettings_store.get_value()(live override).
- Python 3.11+, type hints everywhere,
from __future__ import annotationsat the top of every module. - Ruff (
pyproject.toml): line length 110, targetpy311, rule setsE, F, W, I, B, UP, SIM, RUF, C4, PIE, T20; ignoredB008,E501,RUF012,SIM108,B904,RUF001-RUF003,T201; per-file ignores fortests/*,examples/*andaisrf/analysis/analyzers/*; excludesdocs/images,sdk,.venv.ruff format --checkruns onaisrf/integrationsandexamplesinmake lint. - Style guard (
scripts/check_style.py,tests/test_style.py, CI jobstyle guard): no em dash character (U+2014) anywhere in the tree, no merge markers, no trailing whitespace, no two consecutive blank lines in Markdown, YAML, TOML, TXT, HTML, CSS, JS, TS and shell files, no three consecutive blank lines in Python, andCode_review_Data/must never be tracked. - Keep modules compact; no unnecessary blank lines.
- Never log or store raw secrets:
aisrf.security.redact_headersfor headers, Fernet helpers for upstream credentials,aisrf.codereview.secrets.mask_secretsfor snippets. - Analyzers never raise into the gateway; keep them pure and bounded by
MAX_SCAN_CHARS. - Every privileged action goes through
aisrf.audit.service.record. - Do not change
pyproject.tomldependencies without discussing it in the PR; the core stays free of heavyweight runtime dependencies (optional engines go behind extras and lazy imports). - Client-side modules (
aisrf/integrations/*) must import without optional extras.
Run before pushing:
make lint # ruff check, ruff format --check (integrations, examples), scripts/check_style.py
make test # .venv/bin/python -m pytest -q
node sdk/node/test.js- Subject line:
Area: what changed, imperative or descriptive, no trailing period, no em dashes. Areas used in history:Tests,Release,CI,Docs,Gateway,Dashboard, or a plain sentence for cross-cutting work (for exampleNative binaries for Windows, macOS and Linux, desktop mode, install scripts, ...). - One logical change per commit; keep PRs focused and branched from
main. - Add or update tests and docs for behaviour changes:
docs/API.mdfor endpoints,docs/CONFIGURATION.mdand.env.examplefor settings, the matching wiki page. - Add a line to the
Unreleasedsection ofCHANGELOG.mdunderAdded,Changed,Deprecated,Removed,FixedorSecurityfor user-visible changes; label the PRskip-changelogotherwise. - Describe the security impact in the PR description when the change touches the gateway path, authentication, the audit log or credential handling.
- CI on every PR: style guard, ruff and pytest with coverage on 3.11 and 3.12, binary smoke build, Node self-test, Docker build, CodeQL (python and javascript-typescript), dependency review (fails on high severity). Release commits are
Release X.Y.Zwith tagvX.Y.Z(Releasing).
AISRF, AI Security & Research Framework. github.com/keyuraghao/aisrf, Apache License 2.0.
Start
Gateway
- Gateway-Endpoints-and-Headers
- Request-Normalization
- Policy-Engine
- Agents-and-Credentials
- Configuration-Reference
- Settings-Center
Review
Security analysis
Red teaming
Code review
Interfaces
Operations
Project