RiskForge
EU AI Act Article 9 risk management, as a developer workflow.
RiskForge is an open-source CLI that turns EU AI Act Article 9 compliance from a consultant invoice into a 30-minute developer workflow.
Answer 37 guided questions across 8 EU AI Act risk dimensions. RiskForge produces a tamper-evident Risk Management File (JSON, PDF, or Markdown) carrying a SHA-256 self-verifying digest, suitable for inclusion in your Annex IV technical documentation pack and ready for your legal team and downstream compliance toolchain. (Not a substitute for notified-body conformity assessment.)
Built by AI Exponent LLC. Apache 2.0. Runs entirely offline after pip install.
Documentation: the full documentation covers installation, the command reference with exit codes, the EU AI Act Article 9 mapping, CI integration, plugin authoring, the audit-chain design, and an FAQ.
Quick Start
pip install riskforge
PDF export additionally needs the Pango, cairo, and GDK-PixBuf system libraries (used by WeasyPrint). On Debian/Ubuntu:
apt-get install libpango-1.0-0 libpangocairo-1.0-0 libgdk-pixbuf2.0-0; on macOS:brew install pango. JSON and Markdown export need nothing beyondpip install.
# 1. Register your AI system
riskforge init \
--name "Loan Scoring Model" \
--sys-version "2.1" \
--purpose "Automated credit scoring for retail loan applications." \
--provider "Acme Financial Services" \
--category essential_services
# 2. Record your Article 6(2) Annex III self-classification (required before export)
riskforge system classify <system-id> --confirm
# 3. Run the guided 8-dimension risk assessment (~30 minutes of thinking)
riskforge assess <system-id> \
--assessor-name "Alice Chen" \
--assessor-role "AI Governance Lead"
# For CI or reproducible fixtures, run it non-interactively from a YAML answers file:
# riskforge assess <system-id> -a "Alice Chen" -r "AI Governance Lead" --answers answers.yaml
# 4. (Optional) Record mitigations and accept residual risk
riskforge risk mitigate <system-id> <risk-id> \
-m "Remove postcode feature; add demographic parity monitoring" \
-c preventive --owner "ML Platform" --residual-likelihood 2 --residual-severity 3
riskforge risk accept <system-id> <risk-id> --rationale "Residual within appetite after controls."
# 5. (Optional) Derive Article 9(6)-(8) test requirements per open or knowledge-gap risk
riskforge tests generate <system-id>
# 6. Check completeness before export (exit 1 if a FAIL gate is unmet)
riskforge validate <system-id>
# 7. Export your Article 9 Risk Management File
riskforge export <system-id> --format pdf --output rmf.pdf
riskforge export <system-id> --format json --output rmf.json
# 8. Verify integrity anytime (exit 2 if the file was tampered)
riskforge verify --file rmf.json
Why RiskForge
EU AI Act Article 9 requires providers of high-risk AI systems to maintain a documented risk management system throughout the system's lifecycle.
The current alternatives:
| Option | Cost | Time | Repeatable |
|---|---|---|---|
| Big 4 consulting | €80K–€350K per system¹ | Weeks | No |
| Enterprise GRC platforms | $60K–$200K/year¹ | Months | Partial |
| Spreadsheets | Free | Days | No |
| RiskForge | Free | ~30 min | Yes |
¹ Indicative market figures gathered from public Big-4 governance-engagement quotes and 2024–2026 enterprise GRC pricing pages. Not a benchmark study; your mileage will vary by scope, jurisdiction, and incumbent advisor.
Architecture
RiskForge has four strictly-decoupled layers with CI-enforced import boundaries:
graph TD
CLI["CLI (Typer)<br/>riskforge init / assess / validate / export / verify"]
Engine["Engine Layer<br/>AuditEngine · RiskEngine · ValidateEngine<br/>AssessEngine · ExportEngine · TestDerivationEngine"]
Storage["Storage (FileStore)<br/>YAML + JSONL · chmod 600/700 · async · pluggable ABC"]
Adapters["Integration Adapters<br/>RAGBenchmarkingAdapter · TraceForgeAdapter<br/>Discovered via Python entry_points"]
CLI -->|"calls engine functions"| Engine
Engine -->|"reads/writes via StorageBackend ABC"| Storage
Engine -->|"adapter pattern, no hard imports"| Adapters
style CLI fill:#1e3a5f,color:#fff
style Engine fill:#1e3a5f,color:#fff
style Storage fill:#1e3a5f,color:#fff
style Adapters fill:#1e3a5f,color:#fff
State on disk:
your-project/
├── riskforge.yaml project manifest (chmod 600)
├── .riskforge/ (chmod 700)
│ ├── audit.jsonl append-only hash-chained audit log
│ ├── audit.lock serialises audit appends
│ ├── .nodelete deletion sentinel
│ └── systems/<system-id>/
│ ├── system.yaml
│ ├── register.yaml
│ └── mitigations.yaml
└── rmf-<id>-<export>.json exports land where --output says (default: here)
Plain YAML plus JSONL: readable by regulators without RiskForge installed, and diff-able in GitHub PRs.
AI Exponent compliance toolchain, planned integration
RiskForge is designed to integrate with the broader AI Exponent toolchain. Today, only RiskForge and rag-benchmarking are available on PyPI. The other nodes below are on the public roadmap and will integrate via plain JSON files when they ship.
graph LR
RAG["rag-benchmarking<br/>(accuracy evidence)<br/><i>shipped</i>"]
TF["TraceForge<br/>(data governance)<br/><i>roadmap</i>"]
RF["RiskForge<br/>(Art. 9 RMS)<br/><i>shipped</i>"]
TD["TransparencyDeck<br/>(Art. 13 docs)<br/><i>roadmap</i>"]
CB["ConformityBot<br/>(Art. 43 cert)<br/><i>roadmap</i>"]
CCO["Compliance Officer<br/>(PDF)"]
RAG -->|"benchmark_report.json"| RF
TF -.->|"trace_report.json (planned)"| RF
RF -->|"rmf.pdf"| CCO
RF -.->|"rmf.json (planned)"| TD
RF -.->|"rmf.json (planned)"| CB
style RF fill:#c9a84c,color:#000,stroke:#c9a84c
style RAG fill:#1e3a5f,color:#fff
style TF fill:#6b7685,color:#fff,stroke-dasharray:5
style TD fill:#6b7685,color:#fff,stroke-dasharray:5
style CB fill:#6b7685,color:#fff,stroke-dasharray:5
style CCO fill:#2d5a2d,color:#fff
All current connections are plain JSON files on disk. RiskForge never calls external APIs.
EU AI Act Article 9 Coverage
graph LR
A9_1["Art. 9(1)<br/>Establish RMS"] --> REG["Register lifecycle<br/>Version history<br/>Audit log"]
A9_2a["Art. 9(2)(a)<br/>Identify risks"] --> QB["Guided question bank<br/>8 dimensions · 37 questions"]
A9_2b["Art. 9(2)(b)<br/>Estimate misuse risks"] --> PAT["Risk patterns<br/>6 Annex III scenarios"]
A9_4["Art. 9(4)<br/>Risk measures"] --> MIT["Mitigation docs<br/>Vague-detection"]
A9_6_8["Art. 9(6)–(8)<br/>Testing requirements"] --> TEST["riskforge tests generate<br/>Per-risk metric hints"]
A9_9["Art. 9(9)<br/>Vulnerable groups"] --> VG["Dedicated questions<br/>Mandatory flag"]
A9_10["Art. 9(10)<br/>Documentation"] --> AUD["Append-only JSONL<br/>SHA-256 hash chain"]
style A9_1 fill:#1e3a5f,color:#fff
style A9_2a fill:#1e3a5f,color:#fff
style A9_2b fill:#1e3a5f,color:#fff
style A9_4 fill:#1e3a5f,color:#fff
style A9_6_8 fill:#1e3a5f,color:#fff
style A9_9 fill:#1e3a5f,color:#fff
style A9_10 fill:#1e3a5f,color:#fff
Cross-maps to: NIST AI RMF (GOVERN/MAP/MEASURE/MANAGE) · ISO/IEC 42001 (Clauses 6.1, 8.4, A.6–A.9) · Colorado AI Act (SB 24-205, reset by SB 26-189) · Texas HB 149 (TRAIGA)
Disclaimer: RiskForge produces documented evidence for Article 9 compliance. It does not substitute for qualified legal counsel or notified body conformity assessment.
Validation Gates
Before every export, riskforge validate runs 8 gates:
| Gate | Check |
|---|---|
| G1 | All 8 risk dimensions have at least one entry |
| G2 | Article 6(2) Annex III self-classification documented |
| G3 | All high-scoring risks mitigated or accepted with rationale |
| G4 | Knowledge gaps have test requirements |
| G5 | System metadata complete |
| G6 | Assessor identity recorded |
| G7 | Risk score distribution plausible (warns if all scores are low) |
| G8 | No vague mitigation language detected |
Features
| Feature | Detail |
|---|---|
| Offline-first | Zero outbound calls after pip install, enforced by pytest-socket CI gate |
| Hash-chained audit | Every mutation appended to audit.jsonl with atomic file locks; riskforge verify exits code 2 on tampering |
| Schema-validated exports | Every JSON export validated against rmf.schema.json before writing |
| PDF export | WeasyPrint + Jinja2, no LibreOffice or wkhtmltopdf required |
| Pattern matching | 6 pre-built risk patterns for common Annex III use cases (credit scoring, hiring, facial recognition, medical imaging, content moderation, criminal risk assessment), community contributions extend the library |
| Plugin extensible | Add question banks, exporters, adapters via pip install, no config edit required |
| Git-friendly state | YAML + JSONL files, human-readable, diff-able, merge-conflict-resolvable |
Contributing
The easiest contribution requires zero Python, edit a YAML file and open a PR.
Add a question to an existing dimension:
# src/riskforge/_data/question_bank/privacy.yaml
- id: PRIV-007
text: "Does the system process special category data under GDPR Article 9?"
guidance: "Special category data includes health, biometric, racial, or political data."
annex_iii_categories: [essential_services, employment]
default_likelihood_hint: 3
default_severity_hint: 5
article_refs: ["Art.9(2)(a)", "Art.10(3)"]
nist_rmf_ref: "MAP 1.5"
iso42001_ref: "Clause A.7"
regulatory_status: settled
Add a risk pattern, edit src/riskforge/_data/patterns/patterns.yaml.
Fix a bug or add a feature, see CONTRIBUTING.md.
git clone https://github.com/aiexponenthq/riskforge
cd riskforge
make dev-setup # pip install -e ".[dev]" + pre-commit install
make test # 57 tests, all must pass
make lint # ruff check + format
Privacy
RiskForge makes zero outbound network connections in CLI mode, enforced in CI with pytest-socket --disable-socket.
RiskForge v1.1.2 | Apache 2.0 | Zero telemetry | aiexponent.com
Your AI system's risk data never leaves your machine unless you explicitly deploy the optional API server (pip install riskforge[server]). That server is experimental: it is not security-hardened and is not part of the flagship test suite, so run it only on a trusted, local network.
Releases
| Version | Highlights |
|---|---|
| v1.1.2 | Documentation now points at the in-repo guides (full user guide, Article 9 mapping, audit-chain design with diagrams); the PyPI docs link is absolute and a docs/ index was added. |
| v1.1.1 | Schema hardening: the mandatory not-legal-advice disclosure is now enforced by the RMF JSON Schema (required, non-empty), not only injected at export. |
| v1.1.0 | Release-hardening pass: six correctness fixes, O(n) audit append, worked examples with a determinism harness, a full user guide, benchmarks, and single-source versioning. |
| v1.0.0 | First Production/Stable release. Click 8.3 regression fixed; LICENSE realigned to canonical SPDX; PRD amended to ship reality (37 questions, 6 patterns); coverage floor 24→55. |
| v0.1.4 | CI fixes: lint version compat, format alignment, --sys-version rename |
| v0.1.3 | Superseded by v0.1.4 (ruff format alignment) |
| v0.1.2 | OSS hardening: LICENSE, CONTRIBUTING, SECURITY, issue templates, full integration tests |
| v0.1.1 | riskforge assess fully implemented; PDF exporter fix; audit chain integrity fixes |
| v0.1.0 | Initial release |
License
Apache 2.0, free to use, modify, and distribute.
Built by AI Exponent LLC, hello@aiexponent.com
Part of the AiExponent open-source AI governance toolchain: license-compliance-checker · rag-benchmarking · RiskForge
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file riskforge-1.1.2.tar.gz.
File metadata
- Download URL: riskforge-1.1.2.tar.gz
- Upload date:
- Size: 214.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/6.1.0 CPython/3.12.8
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c218627824076374db8a911e2a4d44e8f1d7ad562d1043e6e834353974f55059
|
|
| MD5 |
0bd19a2b4b571f3c5e19944f8211b8bf
|
|
| BLAKE2b-256 |
eb6331feb2be795a3871dd2f4781f57f1df0a9460190720c4e0184d5e8cc0a32
|
Provenance
The following attestation bundles were made for riskforge-1.1.2.tar.gz:
Publisher:
release.yml on aiexponenthq/riskforge
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
riskforge-1.1.2.tar.gz -
Subject digest:
c218627824076374db8a911e2a4d44e8f1d7ad562d1043e6e834353974f55059 - Sigstore transparency entry: 2200126370
- Sigstore integration time:
-
Permalink:
aiexponenthq/riskforge@3ac652784c4a0ea4b1669ca5b1eba157a78d1805 -
Branch / Tag:
refs/tags/v1.1.2 - Owner: https://github.com/aiexponenthq
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@3ac652784c4a0ea4b1669ca5b1eba157a78d1805 -
Trigger Event:
push
-
Statement type:
File details
Details for the file riskforge-1.1.2-py3-none-any.whl.
File metadata
- Download URL: riskforge-1.1.2-py3-none-any.whl
- Upload date:
- Size: 92.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/6.1.0 CPython/3.12.8
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1788c37560e7d6500548f0e728f6994fda7f6b9de4768719b68665837fc4cbe7
|
|
| MD5 |
5075389c30e9eea42ee6c1174ee12520
|
|
| BLAKE2b-256 |
fa0d490cdb5f6e31e971450497e61ed1e832cdcda69d8d59e0d95ac931a9d8fb
|
Provenance
The following attestation bundles were made for riskforge-1.1.2-py3-none-any.whl:
Publisher:
release.yml on aiexponenthq/riskforge
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
riskforge-1.1.2-py3-none-any.whl -
Subject digest:
1788c37560e7d6500548f0e728f6994fda7f6b9de4768719b68665837fc4cbe7 - Sigstore transparency entry: 2200126400
- Sigstore integration time:
-
Permalink:
aiexponenthq/riskforge@3ac652784c4a0ea4b1669ca5b1eba157a78d1805 -
Branch / Tag:
refs/tags/v1.1.2 - Owner: https://github.com/aiexponenthq
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@3ac652784c4a0ea4b1669ca5b1eba157a78d1805 -
Trigger Event:
push
-
Statement type: