Show me — the diff¶
Two side-by-side code blocks. The audit events each produces. The GRC envelope the second one emits. Done.
Plain ADK agent¶
package com.example.agent;
import com.google.adk.agents.LlmAgent;
import com.google.adk.app.App;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class AgentApplication {
public static void main(String[] args) {
SpringApplication.run(AgentApplication.class, args);
}
LlmAgent rootAgent() {
return LlmAgent.builder()
.name("greeter")
.model("gemini-2.5-flash")
.instruction("Greet the user politely.")
.build();
}
}
Runs. Works. Produces no audit events. No PII redaction. No policy guards. No kill switch. No model-risk gating. No residency enforcement. No evidence flowing to the GRC tool. If a regulator asked you "show me what this agent did to customer 42 last Tuesday," the only honest answer is "I don't know."
Regulus-on-ADK agent¶
package com.example.agent;
import com.google.adk.agents.LlmAgent;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class AgentApplication {
public static void main(String[] args) {
SpringApplication.run(AgentApplication.class, args);
}
// The Regulus Spring starter auto-wires six plugins around the
// LlmAgent based on application.yaml — no extra code needed.
}
application.yaml:
regulus:
compliance:
profiles: [eu-ai-act, uk-gdpr, fca-sysc]
governance:
frameworks: [nist-ai-rmf, nist-ai-rmf-600-1, iso-42001]
grc:
stdout: true
adk:
name: greeter
audit:
sink: stdout
kill-switch:
enabled: true
dual-control: true
residency:
allowed-regions: [europe-west2]
model-risk:
tenant-tier: STANDARD
Same agent. Now every invocation goes through policy guards, PII redaction, model-risk gating, kill-switch check, residency validation, and audit emission — and the evidence flows to your GRC tool.
What you see when a request comes in¶
Inbound: POST /chat with a JWT from your enterprise IdP (Okta /
Auth0 / Keycloak — the included OIDC adapter handles all three; SAML
and mTLS plug in through the same IdentityAdapter SPI) and prompt
"Hello, my NINO is AB123456C, can you confirm?".
The Regulus filter validates the JWT through Spring Security, mints a
canonical Identity (subject + tenant + jurisdiction + purpose +
roles + lawful basis), and binds it to the request thread before any
plugin runs.
The prompt that reaches the model:
The audit event (one of several emitted — model-call shown here):
{
"event_id": "01J6X4ABCDEFG",
"occurred_at": "2026-05-14T11:23:09.123Z",
"actor": "user:42",
"tenant_id": "acme-bank",
"jurisdiction": "EU_UK",
"identity_adapter": "oidc",
"smf_holder": "SMF24:Jane Smith",
"action": "model-call",
"result": "allow",
"model_id": "gemini-2.5-flash",
"model_version": "2026-05-01",
"purpose_code": "greeting-test",
"lawful_basis": "Art. 6(1)(b)",
"regulation_clause": "UK GDPR Art. 25",
"framework_control_id": "A.7.3",
"ai_act_risk_tier": "limited",
"consumer_duty_outcome": "support",
"redactions": ["NINO_1"],
"mechanism": "pii-redaction",
"chain_index": 1284,
"prev_event_hash": "9f3e…",
"event_hash": "1c87…"
}
The tenant_id / jurisdiction / identity_adapter fields come from
the canonical Identity the IdP adapter minted on the inbound side.
The chain_index and prev_event_hash / event_hash fields appear
when regulus.ai.observability.audit.integrity.enabled=true — they
let an auditor run regulus audit verify ./chain.jsonl offline and
prove tamper-evidence without a running Regulus stack.
The same event becomes a GrcEvidenceEnvelope and lands in your GRC
tool (here, stdout for the demo — in production: ServiceNow IRM, OneTrust
AI Governance, or your tool of choice):
{
"event_id": "01J6X4ABCDEFG",
"occurred_at": "2026-05-14T11:23:09.123Z",
"control_framework_id": "iso-42001",
"control_id": "A.7.3",
"compliance_profile_id": "uk-gdpr",
"regulation_clause": "Art. 25",
"kind": "CONTROL_TEST",
"actor": "user:42",
"result": "pass",
"audit_event_link": "regulus-audit://01J6X4ABCDEFG"
}
Your 2L analyst reads this directly from their GRC tool's
control-evidence view and attests the control. Your 3L auditor pulls the
same event by subject_id, six months from now, and reproduces the
walk-through. None of that worked an hour ago.
What's actually firing¶
Each colour-coded plugin contributes one or more events per request:
| ADK callback | Regulus plugin | Effect |
|---|---|---|
BeforeAgentCallback |
RegulusDataResidencyPlugin |
Re-validates region pin (defence in depth) |
BeforeAgentCallback |
RegulusKillSwitchPlugin |
Checks kill state per scope |
BeforeModelCallback |
RegulusPolicyPlugin |
Purpose binding, consent, Art. 22 safeguards |
BeforeModelCallback (mutating) |
RegulusPrivacyPlugin |
Replaces PII with stable tokens |
BeforeModelCallback |
RegulusModelRiskPlugin |
Tier ceiling enforcement |
AfterModelCallback |
RegulusPrivacyPlugin |
Re-redacts streamed output |
AfterModelCallback |
RegulusAuditPlugin |
Emits the audit event above |
| (downstream) | RegulusGovernanceEvidencePlugin |
Builds the GRC envelope, hands to each adapter |
Compose them on the ADK App directly if you prefer code over YAML:
App app = App.builder("greeter", rootAgent)
.plugin(RegulusDataResidencyPlugin.allow("europe-west2"))
.plugin(RegulusKillSwitchPlugin.dualControl())
.plugin(RegulusPolicyPlugin.fromProfile(profile))
.plugin(RegulusPrivacyPlugin.withPatterns(NINO, IBAN, BIC, SORT_CODE).build())
.plugin(RegulusModelRiskPlugin.tier(Tier.STANDARD))
.plugin(RegulusAuditPlugin.forProfile(profile).toKafka("audit.regulus.v1").build())
.plugin(RegulusGovernanceEvidencePlugin.forFramework(framework, adapters, auditSink))
.build();
Both paths produce the same audit shape.
Run it yourself¶
# 60s — install the CLI:
curl -fsSL https://raw.githubusercontent.com/neul-labs/regulus/main/install.sh | sh
# 60s — scaffold a project:
regulus init my-agent \
--profiles=eu-ai-act,uk-gdpr,fca-sysc \
--frameworks=nist-ai-rmf,iso-42001 \
--grc-adapter=stdout
# 5min — run it:
cd my-agent && gradle wrapper && ./gradlew bootRun
Hit POST /chat with a prompt containing a NINO and watch the events
print.
Where to read next¶
- Why Regulus — the broader story.
- Install the CLI — full install paths.
- ADK quickstart — without the scaffold, manual setup.
- Plugins overview — what each plugin does in detail.