4 Governance Domains

Admina organises its governance capabilities into 4 domains. Each domain groups related controls under a single engine with clear boundaries, so you always know what runs, where, and at what latency cost.

D1

Data Sovereignty

Rust + spaCy NER ยท Presidio optional

Ensures that sensitive data never leaves the boundaries you define. PII is automatically detected and redacted, data residency rules are enforced in the SDK, and every data type is classified for compliance tracking.

PII Redaction

Detects and redacts email addresses, phone numbers, credit cards, SSNs, IBANs, IP addresses, and person names (via spaCy NER + regex). The Rust engine processes each scan in 0.62µs median.

Data Residency Enforcement

Restricts data to allowed geographic or logical zones โ€” EU, local, or custom-defined regions. This is an SDK feature: zones are checked by GovernedData(residency_zone=โ€ฆ, allowed_zones=โ€ฆ), which raises PermissionError and emits a BLOCK audit event on every ingest or query that would leave the allowed zones. The governance proxy does not run residency checks on proxied traffic.

Data Classification

Categorizes data types automatically so downstream compliance checks (Domain 4) can match each payload against the correct regulatory requirements.

Patterns detected

EMAILuser@example.com → [EMAIL]
PHONE+39 055 123456 → [PHONE]
CREDIT_CARD4111 1111 1111 1111 → [CREDIT_CARD]
SSN123-45-6789 → [SSN]
IBANIT60X0542811101000000123456 → [IBAN]
IP_ADDRESS192.168.1.1 → [IP_ADDR]
PERSONJohn Smith → [PERSON] (NER)
ORGAcme Corp → [ORG] (NER)
GPE / LOCPisa, Italy → [LOCATION] (NER)

NER (Named Entity Recognition) in the default engine uses spaCy en_core_web_sm. Regex-based patterns work for all languages, and the default engine already covers the Italian codice fiscale and Spanish DNI/NIE by regex. Since v0.11.0 the optional Presidio engine adds Italian NER via it_core_news_sm and maps its own IT/ES identifier recognisers onto the same categories โ€” see Selectable PII engine below.

Graceful fallback (v0.9.2+). spaCy is imported lazily, so the proxy boots without the [nlp] extra. In that case PII redaction runs in regex-only mode (email, phone, credit card, SSN, IBAN, IP address, EU national IDs). Install admina-framework[nlp] and run python -m spacy download en_core_web_sm to enable the NER entities above (PERSON, ORG, GPE/LOC).

Selectable PII engine (v0.11.0)

Since v0.11.0 the PII engine is selectable. The default is unchanged โ€” spacy-regex (spaCy NER + regex, optionally Rust-accelerated). The alternative is Microsoft Presidio, selected with ADMINA_PII_ENGINE=presidio or the top-level pii_engine: presidio key in admina.yaml โ€” a sibling of forensic_store: and auth_provider:, not nested under domains.

Presidio is used analyzer-only: it performs detection, while Admina keeps its own masking. Both engines read their mask tokens from the same table, so the output mask format is identical โ€” [EMAIL], [PERSON], [IBAN] and so on โ€” which gives drop-in mask compatibility between them.

pip install "admina-framework[presidio]"
python -m spacy download en_core_web_sm it_core_news_sm

The extra pulls presidio-analyzer only โ€” Admina does its own masking, so presidio-anonymizer is not installed.

Languages supported are EN and IT, each active only if its spaCy model is actually installed. Install only en_core_web_sm and you silently get an EN-only Presidio engine, with the Italian fiscal-code recogniser absent.

The Presidio โ†’ Admina category mapping covers EMAIL, PHONE, CREDIT_CARD, IBAN, IP_ADDRESS, SSN, PERSON, LOCATION/GPE, ORG, Italian codice fiscale and Spanish DNI/NIE. Categories the default engine has but Presidio cannot emit: date of birth, LOC, and the German Personalausweis (the latter off by default in both engines).

Choose deliberately. Engine selection is fail-fast: an unavailable or misspelled engine raises instead of falling back to the default, and in the proxy that aborts startup โ€” unlike ADMINA_ENGINE=rust, which warns and falls back. Both engines read the built-in category defaults under the proxy โ€” neither path is handed a config object, so a per-deployment narrowing under domains.data_sovereignty.pii.categories in admina.yaml is not applied to proxied traffic; it only takes effect when you construct PIIRedactor(config=โ€ฆ) yourself in the SDK. With Presidio ADMINA_SPACY_MODEL is also ignored (its models are fixed). Selecting a named PII engine also bypasses Rust acceleration, which only ever applies to spacy-regex. For the measured detection comparison between the two engines see the Rust engine guide.
D2

AI Infrastructure

Python (opt-in)

An opt-in domain that provides a fully governed AI stack โ€” LLM serving, RAG pipelines, and a Web UI โ€” all enabled via admina.yaml configuration.

LLM Engine

Abstracts Ollama and vLLM backends with automatic GPU detection (NVIDIA and AMD). Supports hot model switching without downtime โ€” swap models while live traffic continues to be served.

RAG Pipeline

ChromaDB vector store with recursive character and semantic chunking. Ingests multiple formats: PDF, DOCX, HTML, CSV, XML.

Web UI

Open WebUI container with built-in OIDC and LDAP authentication. Provides a chat interface for end-users while all traffic flows through the Admina governance proxy.

Note: This domain is opt-in. Enable it in your admina.yaml under the domains.ai_infra section.
D3

Agent Security

Rust RegexSet

Protects the full agent lifecycle โ€” from prompt injection attacks to runaway loops โ€” with microsecond-latency checks that apply to every agent-to-agent call transiting the proxy.

Anti-Injection Firewall

The default Python engine compiles 33 regex patterns across 9 categories, matched against the raw text and against an evasion-normalised copy (homoglyph, leetspeak, char-by-char, base64, ROT13), plus heuristic scoring โ€” 7.79µs median. With admina-core installed the optional Rust engine runs its own narrower 15-pattern RegexSet in a single pass at 2.08µs median; see the Rust engine guide for the detection trade-off.

The 9 categories โ€” these are the only labels that can appear in detections_by_type, in Prometheus label values, or as valid disabled_categories entries (see MODEL_CARD.md for definitions and known limitations):

instruction_override role_hijack prompt_extraction jailbreak delimiter_injection data_exfiltration tool_abuse obfuscation multilang_evasion

Each category groups several pattern families, which are not reported separately: developer-mode and DAN-mode toggles both report jailbreak, and "what are your instructions" reports prompt_extraction. The Rust engine carries its own coarser 15-label set โ€” it appears only in that engine's matched_patterns, never in the stats API, the Prometheus series or disabled_categories, and two of its labels (new_instructions, roleplay_escape) have no Python equivalent at all. Setting disabled_categories or custom_patterns forces the Python engine so operator rules are actually enforced.

Patterns target English with an explicit multilang_evasion subset for French, Italian, Spanish, German. Contributions for additional locales are welcome.

Custom domain-specific patterns

Add deployment-specific patterns under domains.agent_security.firewall.custom_patterns in admina.yaml โ€” no fork required:

domains:
  agent_security:
    firewall:
      custom_patterns:
        - regex: "delete\\s+user\\s+\\d+"
          category: "destructive_user_op"
          risk_level: high
        - regex: "(production|prod)\\s+(database|db)\\s+drop"
          category: "prod_db_destructive"
          risk_level: critical

Governance reaction mode

Switch how the firewall reacts to flagged traffic, useful for the first 1โ€“2 weeks of a new deployment:

enforceDefault โ€” block every flagged request, at whatever severity
observeNever block; log "would have blocked" for review and FP tuning
dry-runLike observe + tag the response so downstream tools know the request was analysed

Loop Breaker

TF-IDF + cosine similarity on a sliding window of recent requests (configurable threshold 0.85). Latency: 2.38µs. Automatically circuit-breaks sessions before runaway costs or deadlocks occur.

Proxy governance

The full security pipeline applies to all agent-to-agent calls transiting the proxy โ€” not just user-facing requests. Every hop is inspected.

When a pluggable guard raises an exception, the default is to skip it and record an ERROR check; setting ADMINA_GUARD_FAIL_MODE=closed (values open | closed, default open) turns that exception into a BLOCK โ€” see Configuration for the exact scope.

Risk levels

risk_level is not a block threshold. In enforce mode the pipeline branches on whether the firewall matched anything โ€” a single MEDIUM obfuscation pattern blocks exactly like a CRITICAL jailbreak. What the level is for is triage and alert routing: it is the highest severity among the patterns that matched, and it travels on the response, the audit event and the forensic record.

LOWReported when nothing matched โ€” and for any custom_patterns entry you declare at that level, which still blocks
MEDIUM3 patterns โ€” base64 and ROT13 obfuscation markers, plus the "what are your instructions" phrasing of prompt extraction
HIGH15 patterns โ€” role hijack, prompt extraction, data exfiltration, <system>-style delimiter injection, hex-escape obfuscation, internal-API tool abuse
CRITICAL15 patterns โ€” direct instruction override and its IT/FR/ES/DE equivalents, named jailbreaks (DAN & co.), special-token delimiter injection, destructive or shell tool abuse

There is no session-flagging mechanism: a block is per-request, and the session is only interrupted by the loop breaker's circuit breaker. A forensic record is written for every governed request โ€” both /mcp and the OpenAI-compatible gateway โ€” not only for flagged or critical ones.

D4

Compliance

Python + Rust (sha2)

Multi-regulation compliance tooling โ€” EU AI Act classification & gap analysis, NIS2 Art. 21(2) self-assessment, GDPR Art. 30 RoPA registry & Art. 35 DPIA scaffold, plus a hand-curated cross-regulation matrix. Backed by a SHA-256 hash-chained forensic black box and native OpenTelemetry spans.

Decision-support, not legal advice. Admina's compliance modules are self-assessment aids. A passing score in gap_analysis() does not constitute legal compliance and cannot replace conformity assessment under EU AI Act Art. 43, designated NIS2 authority audit, or GDPR DPO review. See the MODEL_CARD for scope, limitations, and known failure modes of every component.

EU AI Act

Automated risk classification under Article 6 and gap analysis against Articles 9โ€“15. Risk categories: unacceptable, high, limited, minimal.

UNACCEPTABLEBanned systems (social scoring, real-time biometrics in public, non-consensual deepfakes / synthetic CSAM โ€” added by Omnibus VII, effective 2 Dec 2026)
HIGHCritical infrastructure, employment, education, law enforcement
LIMITEDChatbots, emotion recognition โ€” transparency obligations apply
MINIMALSpam filters, games โ€” no additional requirements

Timeline โ€” Omnibus VII (Council & Parliament agreement, 7 May 2026)

The Omnibus VII agreement postponed several high-risk deadlines, reduced the Art. 50 transparency grace period, and added a new Art. 5 prohibition. Admina's EU_AI_ACT_DEADLINES table mirrors the agreed timeline.

ObligationOriginal dateEffective date
Art. 5 โ€” prohibitions2025-02-02in force
Art. 50โ€“55 โ€” GPAI obligations2025-08-02in force
Art. 50 โ€” synthetic-content transparency2026-08-02 + 6m2026-12-02
Art. 5 โ€” NCII / synthetic CSAM (NEW)โ€”2026-12-02
Annex III high-risk systems2026-08-022027-12-02
National regulatory sandboxes2026-08-022027-08-02
Annex I high-risk (in products)2027-08-022028-08-02
Full applicationโ€”2028-08-02

Source: Council press release, 7 May 2026. Formal adoption expected before 2 August 2026.

NIS2 โ€” Article 21(2) self-assessment

Deterministic checklist of 10 measure areas ร— 4 controls = 40 checks mirroring the technical and organisational measures required by NIS2 Art. 21(2): policies on risk analysis and information system security; incident handling; business continuity (backup, disaster recovery, crisis management); supply chain security; security in network and IS acquisition, development, and maintenance; policies and procedures to assess effectiveness of measures; basic cyber hygiene practices and cybersecurity training; policies and procedures regarding the use of cryptography; human resources security, access control policies, asset management; multi-factor authentication and secure communications. Gap analysis surfaces unmet controls.

Endpoints: GET /api/compliance/nis2/areas ยท POST /api/compliance/nis2/assess.

GDPR โ€” RoPA & DPIA

Two GDPR components ship with Admina:

  • Article 30 RoPA registry โ€” typed CRUD over Records of Processing Activities. In-memory by default: the config loader has no gdpr section, so the commented ropa_path key in admina.yaml.example is inert. Persistence to a JSON file on disk is opt-in via the ADMINA_GDPR_ROPA_PATH environment variable, or by passing storage_path= when you construct ProcessingActivitiesRegistry yourself. If the path is not writable the registry logs a warning and stays in-memory.
  • Article 35 DPIA template โ€” generates a Markdown scaffold from operator-supplied facts, ready for legal review.

Endpoints: GET / POST / PUT / DELETE /api/compliance/gdpr/records[โ€ฆ] ยท POST /api/compliance/gdpr/dpia/template.

Cross-regulation matrix

Hand-curated mapping of 12 operational controls across EU AI Act, NIS2, and GDPR โ€” single source of truth for controls that satisfy multiple frameworks at once. Useful to avoid duplicate audit work; constant, no configuration. Surfaced at GET /api/compliance/matrix.

Forensic Black Box

SHA-256 hash chain with WORM semantics. Each record links to the previous hash, making any modification immediately detectable. Three backends are available โ€” see Configuration โ†’ Forensic backends for the comparison and the production recommendation.

Signed chain state (v0.11.0). The chain head and record count are cached in a _chain_state.json file (or bucket-root object), so a restart does not have to re-read every record. Set ADMINA_FORENSIC_STATE_KEY to have that file signed with HMAC-SHA256 into a _chain_state.json.sig sidecar. On restore a valid signature is trusted as a fast path; a missing or invalid signature is treated as untrusted โ€” a CRITICAL event is logged and the state is reconstructed from the stored records instead of the potentially rewritten state file. What is signed is the exact byte payload written to the state file โ€” the chain head, the record count and the update timestamp โ€” and the signature is stored as a hex digest in the sidecar, next to the records on disk or as its own object in the same bucket. This covers ForensicBlackBox on both its filesystem and its S3 backend, and the FilesystemForensicStore plugin as well. With no key set the state file is simply unsigned and the baseline truncation protection (reconstruction from persisted records) is retained โ€” signing is recommended in production, not required. Note that a missing sidecar and a forged one produce the same log line, so the system does not distinguish tampering from an absent signature.

Embedding Admina directly rather than running the proxy? Both classes accept the key as the constructor keyword argument state_signing_key: str | None = None, which takes precedence over ADMINA_FORENSIC_STATE_KEY โ€” the environment variable is consulted only when the argument is omitted or empty:

from admina.domains.compliance.forensic import ForensicBlackBox

# signing_key comes from your own secret manager โ€” never hardcode it
box = ForensicBlackBox(filesystem_dir="/var/lib/admina/forensic", state_signing_key=signing_key)

Record structure

{
  "sequence_number": 1024,
  "timestamp_utc": "2026-05-21T14:23:01.442331+00:00",
  "timestamp_unix_ms": 1779373381442,
  "previous_hash": "9c12...",
  "event": {
    "event_id": "3f2a9c14-5b7e-4d61-9a03-2c8f1e4b7d55",
    "event_type": "mcp_request",
    "agent_id": "openclaw-agent",
    "session_id": "sess_abc123",
    "method": "tools/call",
    "action": "allow",
    "risk_level": "low",
    "governance_latency_ms": 0.42,
    "checks": {"loop_breaker": {...}, "firewall": {...}, "pii_redaction": {...}},
    "would_action": null
  },
  "record_hash": "a3f8..."
}

Everything the proxy submits lives under event; the surrounding keys are the chain metadata added by ForensicBlackBox.record(). Hashes are bare SHA-256 hex digests โ€” no sha256: prefix.

would_action is the shadow decision recorded when ADMINA_GOVERNANCE_MODE is observe or dry-run โ€” the action the pipeline would have taken before the mode downgraded it to ALLOW. The key is always present in the event object of the /mcp record; its value is "block" or "circuit_break" in those modes, and null under enforce mode and on clean traffic. Before v0.11.0 the shadow decision reached only the ClickHouse analytics record, leaving the hash-chained audit trail without it. Caveat: the OpenAI-compatible gateway's forensic record does not carry would_action โ€” the fix landed on /mcp only.

Chain verification

Chain integrity is checked programmatically via ForensicBlackBox.verify_chain(last_n=0) (in domains/compliance/forensic.py). It is a coroutine, so it must be awaited; it reads the records back from the configured backend itself rather than taking them as an argument. Pass last_n > 0 to verify only the tail. The same check backs admina doctor and the GET /api/v1/forensic/verify endpoint.

import asyncio
from admina.domains.compliance.forensic import ForensicBlackBox

box = ForensicBlackBox(filesystem_dir="/var/lib/admina/forensic")
result = asyncio.run(box.verify_chain())
print(result)
# {'valid': True, 'records': 1024, 'last_hash': 'a3f8...'}

To verify a list of records you already hold, use the synchronous verify_records(records), which returns {"valid": bool, "checked": int} (plus "error" when invalid).

OpenTelemetry Integration

Native OTEL spans for all governance decisions. Exports to OTLP gRPC on port 4317. Every domain action is recorded as a span attribute โ€” no code changes required in your agent.

OISG Adequacy Score

The full Docker Compose stack ships OISG adequate out of the box; a bare pip install scores 75, in the Good coverage band. The adequacy score (Open ยท Intelligent ยท Secure ยท Governed, 0โ€“100) is computed automatically from the live runtime state โ€” no manual checkboxes โ€” and is surfaced on the dashboard alongside the Admina Score.

curl http://localhost:8080/api/dashboard/oisg \
  -H "X-API-Key: $ADMINA_API_KEY"
# {"total": 85, "level": "OISG adequate", "pillars": {...}}

โ†’ Full paradigm, pillars, and Admina โ†” OISG mapping: OISG Adequacy.