admina-framework 0.9.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- admina/__init__.py +34 -0
- admina/cli/__init__.py +14 -0
- admina/cli/commands/__init__.py +14 -0
- admina/cli/main.py +1522 -0
- admina/cli/templates/admina.yaml.j2 +77 -0
- admina/cli/templates/docker-compose.yml.j2 +254 -0
- admina/cli/templates/env.j2 +10 -0
- admina/cli/templates/main.py.j2 +95 -0
- admina/cli/templates/plugin.py.j2 +145 -0
- admina/cli/templates/plugin_pyproject.toml.j2 +15 -0
- admina/cli/templates/plugin_readme.md.j2 +27 -0
- admina/cli/templates/plugin_test.py.j2 +48 -0
- admina/core/__init__.py +14 -0
- admina/core/config.py +497 -0
- admina/core/event_bus.py +112 -0
- admina/core/secrets.py +257 -0
- admina/core/types.py +146 -0
- admina/dashboard/__init__.py +8 -0
- admina/dashboard/static/heimdall.png +0 -0
- admina/dashboard/static/index.html +1045 -0
- admina/dashboard/static/vendor/alpinejs.min.js +5 -0
- admina/domains/__init__.py +14 -0
- admina/domains/agent_security/__init__.py +41 -0
- admina/domains/agent_security/firewall.py +634 -0
- admina/domains/agent_security/loop_breaker.py +176 -0
- admina/domains/ai_infra/__init__.py +79 -0
- admina/domains/ai_infra/llm_engine.py +477 -0
- admina/domains/ai_infra/rag.py +817 -0
- admina/domains/ai_infra/webui.py +292 -0
- admina/domains/compliance/__init__.py +109 -0
- admina/domains/compliance/cross_regulation.py +314 -0
- admina/domains/compliance/eu_ai_act.py +367 -0
- admina/domains/compliance/forensic.py +380 -0
- admina/domains/compliance/gdpr.py +331 -0
- admina/domains/compliance/nis2.py +258 -0
- admina/domains/compliance/oisg.py +658 -0
- admina/domains/compliance/otel.py +101 -0
- admina/domains/data_sovereignty/__init__.py +42 -0
- admina/domains/data_sovereignty/classification.py +102 -0
- admina/domains/data_sovereignty/pii.py +260 -0
- admina/domains/data_sovereignty/residency.py +121 -0
- admina/integrations/__init__.py +14 -0
- admina/integrations/_engines.py +63 -0
- admina/integrations/cheshirecat/__init__.py +13 -0
- admina/integrations/cheshirecat/admina-plugin/admina_governance.py +207 -0
- admina/integrations/crewai/__init__.py +13 -0
- admina/integrations/crewai/callbacks.py +347 -0
- admina/integrations/langchain/__init__.py +13 -0
- admina/integrations/langchain/callbacks.py +341 -0
- admina/integrations/n8n/__init__.py +14 -0
- admina/integrations/openclaw/__init__.py +14 -0
- admina/plugins/__init__.py +49 -0
- admina/plugins/base.py +633 -0
- admina/plugins/builtin/__init__.py +14 -0
- admina/plugins/builtin/adapters/__init__.py +14 -0
- admina/plugins/builtin/adapters/ollama.py +120 -0
- admina/plugins/builtin/adapters/openai.py +138 -0
- admina/plugins/builtin/alerts/__init__.py +14 -0
- admina/plugins/builtin/alerts/log.py +66 -0
- admina/plugins/builtin/alerts/webhook.py +102 -0
- admina/plugins/builtin/auth/__init__.py +14 -0
- admina/plugins/builtin/auth/apikey.py +138 -0
- admina/plugins/builtin/compliance/__init__.py +14 -0
- admina/plugins/builtin/compliance/eu_ai_act.py +202 -0
- admina/plugins/builtin/connectors/__init__.py +14 -0
- admina/plugins/builtin/connectors/chromadb.py +137 -0
- admina/plugins/builtin/connectors/filesystem.py +111 -0
- admina/plugins/builtin/forensic/__init__.py +14 -0
- admina/plugins/builtin/forensic/filesystem.py +163 -0
- admina/plugins/builtin/forensic/minio.py +180 -0
- admina/plugins/builtin/guards/__init__.py +0 -0
- admina/plugins/builtin/guards/guardrailsai_guard.py +172 -0
- admina/plugins/builtin/pii/__init__.py +14 -0
- admina/plugins/builtin/pii/spacy_regex.py +160 -0
- admina/plugins/builtin/transports/__init__.py +14 -0
- admina/plugins/builtin/transports/http_rest.py +97 -0
- admina/plugins/builtin/transports/mcp.py +173 -0
- admina/plugins/registry.py +356 -0
- admina/proxy/__init__.py +15 -0
- admina/proxy/api/__init__.py +17 -0
- admina/proxy/api/dashboard.py +925 -0
- admina/proxy/api/integration.py +153 -0
- admina/proxy/config.py +214 -0
- admina/proxy/engine_bridge.py +306 -0
- admina/proxy/governance.py +232 -0
- admina/proxy/main.py +1484 -0
- admina/proxy/multi_upstream.py +156 -0
- admina/proxy/state.py +97 -0
- admina/py.typed +0 -0
- admina/sdk/__init__.py +34 -0
- admina/sdk/_compat.py +43 -0
- admina/sdk/compliance_kit.py +359 -0
- admina/sdk/governed_agent.py +391 -0
- admina/sdk/governed_data.py +434 -0
- admina/sdk/governed_model.py +241 -0
- admina_framework-0.9.0.dist-info/METADATA +575 -0
- admina_framework-0.9.0.dist-info/RECORD +102 -0
- admina_framework-0.9.0.dist-info/WHEEL +5 -0
- admina_framework-0.9.0.dist-info/entry_points.txt +2 -0
- admina_framework-0.9.0.dist-info/licenses/LICENSE +191 -0
- admina_framework-0.9.0.dist-info/licenses/NOTICE +16 -0
- admina_framework-0.9.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,658 @@
|
|
|
1
|
+
# Copyright © 2025–2026 Stefano Noferi & Admina contributors
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
|
|
15
|
+
"""Admina — OISG Adequacy Score.
|
|
16
|
+
|
|
17
|
+
Implements the OISG (Open Intelligent Secure Governed) adequacy test
|
|
18
|
+
as defined at https://oisg.ai. The score is computed automatically by
|
|
19
|
+
inspecting the live runtime state of the Admina proxy — no manual
|
|
20
|
+
checkboxes required.
|
|
21
|
+
|
|
22
|
+
Each of the four pillars has 5 criteria worth 5 points each, for a
|
|
23
|
+
total score of 0–100.
|
|
24
|
+
|
|
25
|
+
Score levels:
|
|
26
|
+
0–24 Critical gaps
|
|
27
|
+
25–49 Partial coverage
|
|
28
|
+
50–79 Good coverage
|
|
29
|
+
80–100 OISG adequate
|
|
30
|
+
"""
|
|
31
|
+
|
|
32
|
+
from __future__ import annotations
|
|
33
|
+
|
|
34
|
+
import logging
|
|
35
|
+
from dataclasses import dataclass, field
|
|
36
|
+
from datetime import UTC, datetime
|
|
37
|
+
from typing import Any
|
|
38
|
+
|
|
39
|
+
logger = logging.getLogger("admina.compliance.oisg")
|
|
40
|
+
|
|
41
|
+
# ── Pillar colours (matching oisg.ai design system) ─────────
|
|
42
|
+
PILLAR_COLORS = {
|
|
43
|
+
"open": {"light": "#0F6E56", "dark": "#5DCAA5"},
|
|
44
|
+
"intelligent": {"light": "#534AB7", "dark": "#AFA9EC"},
|
|
45
|
+
"secure": {"light": "#993C1D", "dark": "#F0997B"},
|
|
46
|
+
"governed": {"light": "#185FA5", "dark": "#85B7EB"},
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
POINTS_PER_CRITERION = 5
|
|
50
|
+
CRITERIA_PER_PILLAR = 5
|
|
51
|
+
MAX_PILLAR_SCORE = POINTS_PER_CRITERION * CRITERIA_PER_PILLAR # 25
|
|
52
|
+
MAX_TOTAL_SCORE = MAX_PILLAR_SCORE * 4 # 100
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
# ── Criteria definitions ────────────────────────────────────
|
|
56
|
+
|
|
57
|
+
CRITERIA: dict[str, list[dict[str, str]]] = {
|
|
58
|
+
"open": [
|
|
59
|
+
{
|
|
60
|
+
"id": "o1",
|
|
61
|
+
"label": "Model documentation (capabilities, limitations, provenance) "
|
|
62
|
+
"is available to independent auditors",
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
"id": "o2",
|
|
66
|
+
"label": "Governance infrastructure (policy engines, decision logic) "
|
|
67
|
+
"is open and auditable",
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
"id": "o3",
|
|
71
|
+
"label": "Communication protocols use open standards (MCP, OpenTelemetry, A2A)",
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
"id": "o4",
|
|
75
|
+
"label": "Open projects have community stewardship "
|
|
76
|
+
"(contribution process, security disclosure, governance)",
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
"id": "o5",
|
|
80
|
+
"label": "Model provenance and training methodology are documented and reproducible",
|
|
81
|
+
},
|
|
82
|
+
],
|
|
83
|
+
"intelligent": [
|
|
84
|
+
{
|
|
85
|
+
"id": "i1",
|
|
86
|
+
"label": "Model capabilities are measured with benchmark results, "
|
|
87
|
+
"known failure modes, and confidence calibration",
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
"id": "i2",
|
|
91
|
+
"label": "Infrastructure supports sovereign execution "
|
|
92
|
+
"(on-premise, private cloud, air-gapped) where required",
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
"id": "i3",
|
|
96
|
+
"label": "RAG pipelines are traceable "
|
|
97
|
+
"(document version, embedding model, retrieval path)",
|
|
98
|
+
},
|
|
99
|
+
{
|
|
100
|
+
"id": "i4",
|
|
101
|
+
"label": "Agent autonomy scope is explicit, machine-readable, and enforced at runtime",
|
|
102
|
+
},
|
|
103
|
+
{
|
|
104
|
+
"id": "i5",
|
|
105
|
+
"label": "System can produce on demand a complete explanation "
|
|
106
|
+
"of why it gave a specific response",
|
|
107
|
+
},
|
|
108
|
+
],
|
|
109
|
+
"secure": [
|
|
110
|
+
{
|
|
111
|
+
"id": "s1",
|
|
112
|
+
"label": "Bidirectional injection defence operates on both request and response paths",
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
"id": "s2",
|
|
116
|
+
"label": "Agent identities are cryptographically verifiable (DIDs, Ed25519 key pairs)",
|
|
117
|
+
},
|
|
118
|
+
{
|
|
119
|
+
"id": "s3",
|
|
120
|
+
"label": "Transactional kill switch preserves forensic state and enables rollback",
|
|
121
|
+
},
|
|
122
|
+
{
|
|
123
|
+
"id": "s4",
|
|
124
|
+
"label": "PII redaction is enforced at infrastructure level before model endpoints",
|
|
125
|
+
},
|
|
126
|
+
{
|
|
127
|
+
"id": "s5",
|
|
128
|
+
"label": "Model supply chain integrity is verified "
|
|
129
|
+
"(fingerprinting, SBOM, cryptographic provenance)",
|
|
130
|
+
},
|
|
131
|
+
],
|
|
132
|
+
"governed": [
|
|
133
|
+
{
|
|
134
|
+
"id": "g1",
|
|
135
|
+
"label": "Compliance is verified automatically at runtime, not through periodic audits",
|
|
136
|
+
},
|
|
137
|
+
{
|
|
138
|
+
"id": "g2",
|
|
139
|
+
"label": "Immutable forensic log (hash-chained) records all interactions and decisions",
|
|
140
|
+
},
|
|
141
|
+
{
|
|
142
|
+
"id": "g3",
|
|
143
|
+
"label": "Human oversight is architecturally defined "
|
|
144
|
+
"(which decisions, what info, what timeout)",
|
|
145
|
+
},
|
|
146
|
+
{
|
|
147
|
+
"id": "g4",
|
|
148
|
+
"label": "End-to-end observability is in place (distributed tracing, SLOs, dashboards)",
|
|
149
|
+
},
|
|
150
|
+
{
|
|
151
|
+
"id": "g5",
|
|
152
|
+
"label": "Risk classification is proportional, automated, "
|
|
153
|
+
"and auditable as capabilities evolve",
|
|
154
|
+
},
|
|
155
|
+
],
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
@dataclass
|
|
160
|
+
class CriterionResult:
|
|
161
|
+
"""Result of evaluating a single OISG criterion."""
|
|
162
|
+
|
|
163
|
+
id: str
|
|
164
|
+
label: str
|
|
165
|
+
satisfied: bool
|
|
166
|
+
reason: str = ""
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
@dataclass
|
|
170
|
+
class PillarResult:
|
|
171
|
+
"""Score for one OISG pillar."""
|
|
172
|
+
|
|
173
|
+
name: str
|
|
174
|
+
score: int # 0–25
|
|
175
|
+
max_score: int = MAX_PILLAR_SCORE
|
|
176
|
+
criteria: list[CriterionResult] = field(default_factory=list)
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
@dataclass
|
|
180
|
+
class OISGResult:
|
|
181
|
+
"""Full OISG adequacy score."""
|
|
182
|
+
|
|
183
|
+
total: int # 0–100
|
|
184
|
+
max_total: int = MAX_TOTAL_SCORE
|
|
185
|
+
level: str = ""
|
|
186
|
+
pillars: dict[str, PillarResult] = field(default_factory=dict)
|
|
187
|
+
computed_at: str = ""
|
|
188
|
+
|
|
189
|
+
def to_dict(self) -> dict[str, Any]:
|
|
190
|
+
return {
|
|
191
|
+
"total": self.total,
|
|
192
|
+
"max_total": self.max_total,
|
|
193
|
+
"level": self.level,
|
|
194
|
+
"pillars": {
|
|
195
|
+
name: {
|
|
196
|
+
"name": p.name,
|
|
197
|
+
"score": p.score,
|
|
198
|
+
"max_score": p.max_score,
|
|
199
|
+
"criteria": [
|
|
200
|
+
{
|
|
201
|
+
"id": c.id,
|
|
202
|
+
"label": c.label,
|
|
203
|
+
"satisfied": c.satisfied,
|
|
204
|
+
"reason": c.reason,
|
|
205
|
+
}
|
|
206
|
+
for c in p.criteria
|
|
207
|
+
],
|
|
208
|
+
}
|
|
209
|
+
for name, p in self.pillars.items()
|
|
210
|
+
},
|
|
211
|
+
"computed_at": self.computed_at,
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
def get_level(total: int) -> str:
|
|
216
|
+
"""Classify total score into an OISG adequacy level."""
|
|
217
|
+
if total >= 80:
|
|
218
|
+
return "OISG adequate"
|
|
219
|
+
if total >= 50:
|
|
220
|
+
return "Good coverage"
|
|
221
|
+
if total >= 25:
|
|
222
|
+
return "Partial coverage"
|
|
223
|
+
return "Critical gaps"
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
def compute_oisg_score(
|
|
227
|
+
*,
|
|
228
|
+
firewall: Any | None = None,
|
|
229
|
+
pii_redactor: Any | None = None,
|
|
230
|
+
loop_breaker: Any | None = None,
|
|
231
|
+
forensic_box: Any | None = None,
|
|
232
|
+
compliance_engine: Any | None = None,
|
|
233
|
+
otel_exporter: Any | None = None,
|
|
234
|
+
governance_guards: list | None = None,
|
|
235
|
+
config: Any | None = None,
|
|
236
|
+
engine_status: dict[str, Any] | None = None,
|
|
237
|
+
metrics: dict[str, Any] | None = None,
|
|
238
|
+
) -> OISGResult:
|
|
239
|
+
"""Compute the OISG adequacy score from Admina's live runtime state.
|
|
240
|
+
|
|
241
|
+
Each criterion is evaluated by inspecting whether the corresponding
|
|
242
|
+
Admina subsystem is active and properly configured.
|
|
243
|
+
"""
|
|
244
|
+
if governance_guards is None:
|
|
245
|
+
governance_guards = []
|
|
246
|
+
if engine_status is None:
|
|
247
|
+
engine_status = {}
|
|
248
|
+
if metrics is None:
|
|
249
|
+
metrics = {}
|
|
250
|
+
|
|
251
|
+
pillars: dict[str, PillarResult] = {}
|
|
252
|
+
|
|
253
|
+
# ── O — Open ────────────────────────────────────────────
|
|
254
|
+
o_criteria = _evaluate_open(
|
|
255
|
+
config=config,
|
|
256
|
+
otel_exporter=otel_exporter,
|
|
257
|
+
engine_status=engine_status,
|
|
258
|
+
)
|
|
259
|
+
pillars["open"] = _build_pillar("Open", o_criteria)
|
|
260
|
+
|
|
261
|
+
# ── I — Intelligent ─────────────────────────────────────
|
|
262
|
+
i_criteria = _evaluate_intelligent(
|
|
263
|
+
config=config,
|
|
264
|
+
firewall=firewall,
|
|
265
|
+
loop_breaker=loop_breaker,
|
|
266
|
+
governance_guards=governance_guards,
|
|
267
|
+
forensic_box=forensic_box,
|
|
268
|
+
)
|
|
269
|
+
pillars["intelligent"] = _build_pillar("Intelligent", i_criteria)
|
|
270
|
+
|
|
271
|
+
# ── S — Secure ──────────────────────────────────────────
|
|
272
|
+
s_criteria = _evaluate_secure(
|
|
273
|
+
firewall=firewall,
|
|
274
|
+
pii_redactor=pii_redactor,
|
|
275
|
+
forensic_box=forensic_box,
|
|
276
|
+
config=config,
|
|
277
|
+
engine_status=engine_status,
|
|
278
|
+
)
|
|
279
|
+
pillars["secure"] = _build_pillar("Secure", s_criteria)
|
|
280
|
+
|
|
281
|
+
# ── G — Governed ────────────────────────────────────────
|
|
282
|
+
g_criteria = _evaluate_governed(
|
|
283
|
+
compliance_engine=compliance_engine,
|
|
284
|
+
forensic_box=forensic_box,
|
|
285
|
+
otel_exporter=otel_exporter,
|
|
286
|
+
governance_guards=governance_guards,
|
|
287
|
+
config=config,
|
|
288
|
+
)
|
|
289
|
+
pillars["governed"] = _build_pillar("Governed", g_criteria)
|
|
290
|
+
|
|
291
|
+
total = sum(p.score for p in pillars.values())
|
|
292
|
+
return OISGResult(
|
|
293
|
+
total=total,
|
|
294
|
+
level=get_level(total),
|
|
295
|
+
pillars=pillars,
|
|
296
|
+
computed_at=datetime.now(UTC).isoformat(),
|
|
297
|
+
)
|
|
298
|
+
|
|
299
|
+
|
|
300
|
+
# ── Pillar evaluation helpers ───────────────────────────────
|
|
301
|
+
|
|
302
|
+
|
|
303
|
+
def _build_pillar(name: str, criteria: list[CriterionResult]) -> PillarResult:
|
|
304
|
+
score = sum(POINTS_PER_CRITERION for c in criteria if c.satisfied)
|
|
305
|
+
return PillarResult(name=name, score=score, criteria=criteria)
|
|
306
|
+
|
|
307
|
+
|
|
308
|
+
def _evaluate_open(
|
|
309
|
+
*,
|
|
310
|
+
config: Any | None,
|
|
311
|
+
otel_exporter: Any | None,
|
|
312
|
+
engine_status: dict[str, Any],
|
|
313
|
+
) -> list[CriterionResult]:
|
|
314
|
+
defs = CRITERIA["open"]
|
|
315
|
+
results: list[CriterionResult] = []
|
|
316
|
+
|
|
317
|
+
# O1: Model documentation available — satisfied if ai_infra is
|
|
318
|
+
# enabled (model endpoints expose capabilities)
|
|
319
|
+
ai_enabled = False
|
|
320
|
+
if config is not None:
|
|
321
|
+
ai_cfg = getattr(config, "ai_infra", None)
|
|
322
|
+
if ai_cfg is not None:
|
|
323
|
+
ai_enabled = getattr(ai_cfg, "enabled", False)
|
|
324
|
+
results.append(
|
|
325
|
+
CriterionResult(
|
|
326
|
+
id=defs[0]["id"],
|
|
327
|
+
label=defs[0]["label"],
|
|
328
|
+
satisfied=ai_enabled,
|
|
329
|
+
reason="AI infra enabled — model info endpoint active"
|
|
330
|
+
if ai_enabled
|
|
331
|
+
else "AI infra not enabled — no model documentation exposed",
|
|
332
|
+
)
|
|
333
|
+
)
|
|
334
|
+
|
|
335
|
+
# O2: Governance infrastructure open and auditable — always true
|
|
336
|
+
# (Admina is Apache 2.0 open source)
|
|
337
|
+
results.append(
|
|
338
|
+
CriterionResult(
|
|
339
|
+
id=defs[1]["id"],
|
|
340
|
+
label=defs[1]["label"],
|
|
341
|
+
satisfied=True,
|
|
342
|
+
reason="Admina governance engine is open source (Apache 2.0)",
|
|
343
|
+
)
|
|
344
|
+
)
|
|
345
|
+
|
|
346
|
+
# O3: Open standards (MCP, OpenTelemetry, A2A) — satisfied when
|
|
347
|
+
# the proxy is running (always MCP) and OTEL is configured
|
|
348
|
+
has_otel = otel_exporter is not None
|
|
349
|
+
results.append(
|
|
350
|
+
CriterionResult(
|
|
351
|
+
id=defs[2]["id"],
|
|
352
|
+
label=defs[2]["label"],
|
|
353
|
+
satisfied=True, # proxy always speaks MCP
|
|
354
|
+
reason="Proxy uses MCP protocol"
|
|
355
|
+
+ ("; OTEL exporter active" if has_otel else "; OTEL not configured"),
|
|
356
|
+
)
|
|
357
|
+
)
|
|
358
|
+
|
|
359
|
+
# O4: Community stewardship — always true (Apache 2.0, CONTRIBUTING,
|
|
360
|
+
# SECURITY.md)
|
|
361
|
+
results.append(
|
|
362
|
+
CriterionResult(
|
|
363
|
+
id=defs[3]["id"],
|
|
364
|
+
label=defs[3]["label"],
|
|
365
|
+
satisfied=True,
|
|
366
|
+
reason="Apache 2.0 license, contribution process, security disclosure policy",
|
|
367
|
+
)
|
|
368
|
+
)
|
|
369
|
+
|
|
370
|
+
# O5: Model provenance documented — satisfied if engine status
|
|
371
|
+
# reports version info
|
|
372
|
+
has_version = bool(engine_status.get("rust_version") or engine_status.get("version"))
|
|
373
|
+
results.append(
|
|
374
|
+
CriterionResult(
|
|
375
|
+
id=defs[4]["id"],
|
|
376
|
+
label=defs[4]["label"],
|
|
377
|
+
satisfied=has_version,
|
|
378
|
+
reason="Engine version tracked"
|
|
379
|
+
if has_version
|
|
380
|
+
else "No engine version information available",
|
|
381
|
+
)
|
|
382
|
+
)
|
|
383
|
+
|
|
384
|
+
return results
|
|
385
|
+
|
|
386
|
+
|
|
387
|
+
def _evaluate_intelligent(
|
|
388
|
+
*,
|
|
389
|
+
config: Any | None,
|
|
390
|
+
firewall: Any | None,
|
|
391
|
+
loop_breaker: Any | None,
|
|
392
|
+
governance_guards: list,
|
|
393
|
+
forensic_box: Any | None,
|
|
394
|
+
) -> list[CriterionResult]:
|
|
395
|
+
defs = CRITERIA["intelligent"]
|
|
396
|
+
results: list[CriterionResult] = []
|
|
397
|
+
|
|
398
|
+
# I1: Capabilities measured — satisfied if governance engine
|
|
399
|
+
# benchmarks exist (engine is benchmarked in CI)
|
|
400
|
+
# We check for the presence of firewall + loop_breaker as
|
|
401
|
+
# evidence of measured, calibrated detection thresholds.
|
|
402
|
+
has_calibrated = firewall is not None and loop_breaker is not None
|
|
403
|
+
results.append(
|
|
404
|
+
CriterionResult(
|
|
405
|
+
id=defs[0]["id"],
|
|
406
|
+
label=defs[0]["label"],
|
|
407
|
+
satisfied=has_calibrated,
|
|
408
|
+
reason="Firewall and loop breaker active with calibrated thresholds"
|
|
409
|
+
if has_calibrated
|
|
410
|
+
else "Detection engines not fully configured",
|
|
411
|
+
)
|
|
412
|
+
)
|
|
413
|
+
|
|
414
|
+
# I2: Sovereign execution — always true (Admina runs entirely
|
|
415
|
+
# on-premise, no cloud dependency)
|
|
416
|
+
results.append(
|
|
417
|
+
CriterionResult(
|
|
418
|
+
id=defs[1]["id"],
|
|
419
|
+
label=defs[1]["label"],
|
|
420
|
+
satisfied=True,
|
|
421
|
+
reason="Admina runs entirely on-premise — sovereign by design",
|
|
422
|
+
)
|
|
423
|
+
)
|
|
424
|
+
|
|
425
|
+
# I3: RAG pipelines traceable — satisfied if RAG is enabled
|
|
426
|
+
rag_enabled = False
|
|
427
|
+
if config is not None:
|
|
428
|
+
ai_cfg = getattr(config, "ai_infra", None)
|
|
429
|
+
if ai_cfg is not None and getattr(ai_cfg, "enabled", False):
|
|
430
|
+
rag_cfg = getattr(ai_cfg, "rag", None)
|
|
431
|
+
if rag_cfg is not None:
|
|
432
|
+
rag_enabled = getattr(rag_cfg, "enabled", False)
|
|
433
|
+
results.append(
|
|
434
|
+
CriterionResult(
|
|
435
|
+
id=defs[2]["id"],
|
|
436
|
+
label=defs[2]["label"],
|
|
437
|
+
satisfied=rag_enabled,
|
|
438
|
+
reason="RAG pipeline enabled with traceable config"
|
|
439
|
+
if rag_enabled
|
|
440
|
+
else "RAG pipeline not enabled",
|
|
441
|
+
)
|
|
442
|
+
)
|
|
443
|
+
|
|
444
|
+
# I4: Agent autonomy enforced at runtime — satisfied if
|
|
445
|
+
# governance pipeline is active (firewall + guards)
|
|
446
|
+
# The governance pipeline itself enforces bounded autonomy
|
|
447
|
+
results.append(
|
|
448
|
+
CriterionResult(
|
|
449
|
+
id=defs[3]["id"],
|
|
450
|
+
label=defs[3]["label"],
|
|
451
|
+
satisfied=firewall is not None,
|
|
452
|
+
reason="Governance pipeline enforces agent autonomy scope"
|
|
453
|
+
if firewall is not None
|
|
454
|
+
else "Governance pipeline not active",
|
|
455
|
+
)
|
|
456
|
+
)
|
|
457
|
+
|
|
458
|
+
# I5: Explainability on demand — satisfied if forensic box is
|
|
459
|
+
# active (complete decision trace available)
|
|
460
|
+
has_explainability = forensic_box is not None
|
|
461
|
+
results.append(
|
|
462
|
+
CriterionResult(
|
|
463
|
+
id=defs[4]["id"],
|
|
464
|
+
label=defs[4]["label"],
|
|
465
|
+
satisfied=has_explainability,
|
|
466
|
+
reason="Forensic black box provides full decision trace"
|
|
467
|
+
if has_explainability
|
|
468
|
+
else "No forensic box — explainability not available",
|
|
469
|
+
)
|
|
470
|
+
)
|
|
471
|
+
|
|
472
|
+
return results
|
|
473
|
+
|
|
474
|
+
|
|
475
|
+
def _evaluate_secure(
|
|
476
|
+
*,
|
|
477
|
+
firewall: Any | None,
|
|
478
|
+
pii_redactor: Any | None,
|
|
479
|
+
forensic_box: Any | None,
|
|
480
|
+
config: Any | None,
|
|
481
|
+
engine_status: dict[str, Any],
|
|
482
|
+
) -> list[CriterionResult]:
|
|
483
|
+
defs = CRITERIA["secure"]
|
|
484
|
+
results: list[CriterionResult] = []
|
|
485
|
+
|
|
486
|
+
# S1: Bidirectional injection defence — satisfied if firewall
|
|
487
|
+
# is active (scans both inbound and outbound in pipeline)
|
|
488
|
+
fw_active = firewall is not None
|
|
489
|
+
results.append(
|
|
490
|
+
CriterionResult(
|
|
491
|
+
id=defs[0]["id"],
|
|
492
|
+
label=defs[0]["label"],
|
|
493
|
+
satisfied=fw_active,
|
|
494
|
+
reason="Anti-injection firewall active on request path"
|
|
495
|
+
if fw_active
|
|
496
|
+
else "Firewall not configured",
|
|
497
|
+
)
|
|
498
|
+
)
|
|
499
|
+
|
|
500
|
+
# S2: Cryptographic agent identity — satisfied if API key auth
|
|
501
|
+
# is configured (first step toward cryptographic identity)
|
|
502
|
+
has_auth = False
|
|
503
|
+
if config is not None:
|
|
504
|
+
api_key = getattr(config, "admina_api_key", "")
|
|
505
|
+
has_auth = bool(api_key)
|
|
506
|
+
results.append(
|
|
507
|
+
CriterionResult(
|
|
508
|
+
id=defs[1]["id"],
|
|
509
|
+
label=defs[1]["label"],
|
|
510
|
+
satisfied=has_auth,
|
|
511
|
+
reason="API key authentication configured"
|
|
512
|
+
if has_auth
|
|
513
|
+
else "No agent authentication configured",
|
|
514
|
+
)
|
|
515
|
+
)
|
|
516
|
+
|
|
517
|
+
# S3: Kill switch preserves forensic state — satisfied if
|
|
518
|
+
# circuit_break action exists and forensic box is active
|
|
519
|
+
has_kill_switch = forensic_box is not None
|
|
520
|
+
results.append(
|
|
521
|
+
CriterionResult(
|
|
522
|
+
id=defs[2]["id"],
|
|
523
|
+
label=defs[2]["label"],
|
|
524
|
+
satisfied=has_kill_switch,
|
|
525
|
+
reason="Circuit breaker + forensic black box active"
|
|
526
|
+
if has_kill_switch
|
|
527
|
+
else "No forensic box for kill switch state preservation",
|
|
528
|
+
)
|
|
529
|
+
)
|
|
530
|
+
|
|
531
|
+
# S4: PII redaction at infrastructure level — satisfied if
|
|
532
|
+
# PII redactor is enabled
|
|
533
|
+
pii_active = pii_redactor is not None
|
|
534
|
+
results.append(
|
|
535
|
+
CriterionResult(
|
|
536
|
+
id=defs[3]["id"],
|
|
537
|
+
label=defs[3]["label"],
|
|
538
|
+
satisfied=pii_active,
|
|
539
|
+
reason="PII redaction enforced at proxy level"
|
|
540
|
+
if pii_active
|
|
541
|
+
else "PII redaction not configured",
|
|
542
|
+
)
|
|
543
|
+
)
|
|
544
|
+
|
|
545
|
+
# S5: Model supply chain integrity — satisfied if Rust engine
|
|
546
|
+
# is active (versioned, reproducible builds)
|
|
547
|
+
has_integrity = engine_status.get("rust_available", False)
|
|
548
|
+
results.append(
|
|
549
|
+
CriterionResult(
|
|
550
|
+
id=defs[4]["id"],
|
|
551
|
+
label=defs[4]["label"],
|
|
552
|
+
satisfied=has_integrity,
|
|
553
|
+
reason="Rust engine with versioned builds"
|
|
554
|
+
if has_integrity
|
|
555
|
+
else "No verifiable engine build information",
|
|
556
|
+
)
|
|
557
|
+
)
|
|
558
|
+
|
|
559
|
+
return results
|
|
560
|
+
|
|
561
|
+
|
|
562
|
+
def _evaluate_governed(
|
|
563
|
+
*,
|
|
564
|
+
compliance_engine: Any | None,
|
|
565
|
+
forensic_box: Any | None,
|
|
566
|
+
otel_exporter: Any | None,
|
|
567
|
+
governance_guards: list,
|
|
568
|
+
config: Any | None,
|
|
569
|
+
) -> list[CriterionResult]:
|
|
570
|
+
defs = CRITERIA["governed"]
|
|
571
|
+
results: list[CriterionResult] = []
|
|
572
|
+
|
|
573
|
+
# G1: Runtime compliance verification — satisfied if EU AI Act
|
|
574
|
+
# compliance engine is active
|
|
575
|
+
compliance_active = compliance_engine is not None
|
|
576
|
+
eu_enabled = True
|
|
577
|
+
if config is not None:
|
|
578
|
+
comp_cfg = getattr(config, "compliance", None)
|
|
579
|
+
if comp_cfg is not None:
|
|
580
|
+
eu_enabled = getattr(comp_cfg, "eu_ai_act_enabled", True)
|
|
581
|
+
g1_ok = compliance_active and eu_enabled
|
|
582
|
+
results.append(
|
|
583
|
+
CriterionResult(
|
|
584
|
+
id=defs[0]["id"],
|
|
585
|
+
label=defs[0]["label"],
|
|
586
|
+
satisfied=g1_ok,
|
|
587
|
+
reason="EU AI Act compliance engine active"
|
|
588
|
+
if g1_ok
|
|
589
|
+
else "Compliance engine not active",
|
|
590
|
+
)
|
|
591
|
+
)
|
|
592
|
+
|
|
593
|
+
# G2: Immutable forensic log — satisfied if forensic black box
|
|
594
|
+
# is active with valid chain
|
|
595
|
+
chain_valid = (
|
|
596
|
+
forensic_box is not None and getattr(forensic_box, "chain_head", "GENESIS") != "GENESIS"
|
|
597
|
+
)
|
|
598
|
+
results.append(
|
|
599
|
+
CriterionResult(
|
|
600
|
+
id=defs[1]["id"],
|
|
601
|
+
label=defs[1]["label"],
|
|
602
|
+
satisfied=forensic_box is not None,
|
|
603
|
+
reason="SHA-256 hash-chained forensic black box active"
|
|
604
|
+
+ (" (chain initialised)" if chain_valid else " (chain at GENESIS)")
|
|
605
|
+
if forensic_box is not None
|
|
606
|
+
else "Forensic black box not configured",
|
|
607
|
+
)
|
|
608
|
+
)
|
|
609
|
+
|
|
610
|
+
# G3: Human oversight architecturally defined — satisfied if
|
|
611
|
+
# governance guards or escalation policies are configured
|
|
612
|
+
has_oversight = len(governance_guards) > 0
|
|
613
|
+
results.append(
|
|
614
|
+
CriterionResult(
|
|
615
|
+
id=defs[2]["id"],
|
|
616
|
+
label=defs[2]["label"],
|
|
617
|
+
satisfied=has_oversight,
|
|
618
|
+
reason=f"{len(governance_guards)} governance guard(s) enforce oversight"
|
|
619
|
+
if has_oversight
|
|
620
|
+
else "No governance guards configured for human oversight",
|
|
621
|
+
)
|
|
622
|
+
)
|
|
623
|
+
|
|
624
|
+
# G4: End-to-end observability — satisfied if OTEL exporter
|
|
625
|
+
# and dashboard are configured
|
|
626
|
+
has_otel = otel_exporter is not None
|
|
627
|
+
dashboard_enabled = True
|
|
628
|
+
if config is not None:
|
|
629
|
+
dash_cfg = getattr(config, "dashboard", None)
|
|
630
|
+
if dash_cfg is not None:
|
|
631
|
+
dashboard_enabled = getattr(dash_cfg, "enabled", True)
|
|
632
|
+
g4_ok = has_otel or dashboard_enabled
|
|
633
|
+
results.append(
|
|
634
|
+
CriterionResult(
|
|
635
|
+
id=defs[3]["id"],
|
|
636
|
+
label=defs[3]["label"],
|
|
637
|
+
satisfied=g4_ok,
|
|
638
|
+
reason="Observability active"
|
|
639
|
+
+ (" (OTEL" if has_otel else " (no OTEL")
|
|
640
|
+
+ (", dashboard enabled)" if dashboard_enabled else ", no dashboard)"),
|
|
641
|
+
)
|
|
642
|
+
)
|
|
643
|
+
|
|
644
|
+
# G5: Risk classification automated — satisfied if compliance
|
|
645
|
+
# engine can classify risk
|
|
646
|
+
has_classification = compliance_active and hasattr(compliance_engine, "classify_risk")
|
|
647
|
+
results.append(
|
|
648
|
+
CriterionResult(
|
|
649
|
+
id=defs[4]["id"],
|
|
650
|
+
label=defs[4]["label"],
|
|
651
|
+
satisfied=has_classification,
|
|
652
|
+
reason="Automated risk classification via EU AI Act engine"
|
|
653
|
+
if has_classification
|
|
654
|
+
else "No automated risk classification available",
|
|
655
|
+
)
|
|
656
|
+
)
|
|
657
|
+
|
|
658
|
+
return results
|