Security architecture
A canonical Principal. Protocols as adapters. Audit by construction.
Regulus's security model leads with a clear shape: one internal
primitive (Principal + Claims),
protocol-specific adapters (OIDC today, SAML / mTLS / service-
account JWT via the IdentityAdapter SPI), and a
single audit trail that every component writes through.
The canonical Principal
The internal primitive is a Regulus Principal carrying
tenant, jurisdiction, purpose-of-processing, model-risk tier
authority, and the credentials' expiry. Every plugin reads from
Principal; no plugin sees raw protocol artifacts
(SAML assertions, LDAP DNs, OIDC ID tokens). Adapters mint a
Principal at the trust boundary and discard the
protocol-specific shape on the inside.
Why this matters for compliance. An auditor will ask: "for this DENY decision, who was the principal, and how do we know the credentials hadn't expired?" If your codebase has OIDC claim parsing in one place, SAML attribute extraction in another, and LDAP lookups in a third — that answer is a spreadsheet exercise. With one canonical shape, it's a query.
IdentityAdapter SPI
Each adapter is a single class implementing
IdentityAdapter. Today's shipped adapter is OIDC
(Spring Boot starter
regulus-ai-identity-oidc-spring-boot-starter); SAML,
mTLS, and service-account JWT adapters are on the SPI surface
with deterministic claim mapping. Every adapter must produce the
same minimum claim set: sub, tenant,
jurisdiction, purpose, tier,
expires_at. The kill-switch plugin reads two of these
claims (tier authority + purpose) to gate destructive operations.
RFC 9421 HTTP Message Signatures on A2A
Cross-organisation agent-to-agent calls travel over ADK's A2A
protocol. Regulus wraps the outbound envelope with RFC 9421 HTTP
Message Signatures over (@method, @target-uri, @body,
@created) and verifies on the inbound side before invoking
the receiver agent. Replay protection ships as part of the
envelope: a per-keypair monotonic nonce window plus a configurable
maximum clock skew.
Status today (v0.2.1). The signing surface and replay
protection are wired through the SPI. Ed25519 signing
implementation is on the next-milestone list — current shipping
code raises UnsupportedOperationException if the
signature is requested in production mode. The replay-protection
+ canonicalisation surface is verifiable today.
Audit integrity — SHA-256 chains, optional per-event sigs
Every RegulusEvent carries the SHA-256 hash of the
previous event (within an emitter scope). Chains are
offline-verifiable with regulus audit verify chain.jsonl.
Per-event signatures are an opt-in v0.3 target — today's hash
chain detects tampering deterministically; signed-per-event detects
identity of the tamperer.
Dual-control kill switch
The kill-switch plugin requires two authorised principals
(tier authority claim) to collapse the agent's tool surface. The
collapse is fail-closed: once invoked, every subsequent tool call
returns a structured policy decision KILL_SWITCH_ENGAGED
with both principals' sub claims in the audit event.
Trigger is via the RegulusKillSwitchPlugin or via the
CLI regulus kill-switch engage --agent <name> --auth-token <jwt>.
Trust boundaries
The four trust boundaries explicit in the system:
- External IdP ↔ Regulus IdentityAdapter. Standard protocol surface (OIDC discovery, JWKS rotation).
- ADK runtime ↔ Regulus plugins. Internal JVM trust; assumes the ADK process is uncompromised.
- Regulus plugins ↔ Regulus services. Same JVM, no boundary at runtime; lint-checked via package visibility.
- Regulus ↔ External GRC adapters. HMAC-signed outbound POSTs; verify the bundled key on the receiver side.
Reporting a vulnerability
See the SECURITY.md in the repository ↗ for the disclosure path. We publish CVEs through GitHub's CVE service.