JVM AI Governance
JVM AI governance is the practice of enforcing policy on AI workloads inside the Java or Kotlin application that runs them — not in a separate sidecar, gateway, or Python governance stack.
For teams that have spent years building on Spring Boot, Quarkus, or plain JVM services, the governance question usually arrives like this: "We're introducing agents into our backend. How do we keep control?" The default answer in the industry has been to bolt on an external governance layer. TramAI's answer is different: the enforcement boundary lives in the JVM runtime your developers already use.
Why JVM Teams Should Not Need a Second Governance Stack
A separate governance stack introduces a split-brain problem:
- Split policies — the policy that governs the model call lives in one system; the policy that governs the tool call lives in another. They drift.
- Network boundaries — a request leaves the JVM, crosses to a governance gateway, and the gateway makes decisions without access to the application's data classification, workflow state, or approval context.
- Audit fragmentation — application logs, gateway logs, and provider logs each tell part of the story. Reconstructing a single workflow execution requires joining three systems.
- Framework mismatch — a Python governance stack cannot enforce anything inside a Kotlin coroutine or a Spring bean. It can only observe from the outside.
Governance that runs in-process has full visibility into the execution it governs: the classification of the input, the workflow run, the tool arguments, the approval state, and the policy version — all available at the enforcement point.
What TramAI Enforces In-Process
All of the following are implemented in the current TramAI release line and run inside your JVM application:
Model and Provider Allowlists
val profile = SovereignProfileConfiguration(
allowedModels = setOf("llama3.2", "gpt-4o"),
allowedProviders = setOf("ollama", "openai"),
allowedFallbackProviders = setOf("ollama"),
allowedTools = emptySet(),
allowedPermissions = emptySet(),
providerZones = mapOf(
"ollama" to ProviderTrustZone.LOCAL,
"openai" to ProviderTrustZone.GLOBAL_CLOUD,
),
deploymentMode = SovereignDeploymentMode.STANDARD,
)
Every model, provider, fallback provider, tool, and permission must be explicitly allowed. The sovereign builder validates the whole profile at build time — missing trust zones, unknown providers, and cross-zone routing violations fail before any model is ever called.
Trust Zones and Classification-Aware Routing
Each provider is classified into a trust zone, and data classification constrains routing:
| Classification | Allowed zones | Fallback zones |
|---|---|---|
RESTRICTED | LOCAL only | None (no fallback) |
CONFIDENTIAL | LOCAL, EU_CLOUD | LOCAL, EU_CLOUD |
INTERNAL | Any | Any |
PUBLIC | Any | Any |
A RESTRICTED payload can only be handled by a local model. If the local provider fails, there is no fallback — the runtime does not silently route sensitive data to a global cloud provider.
Tool Permission Policies
Tools are governed at two distinct boundaries: exposure (does the model even learn the tool exists?) and execution (is the call allowed to run?). Decisions are ALLOW, DENY, or REQUIRE_APPROVAL.
val denyingPolicy = PolicyEngine { context ->
if (context.enforcementPoint == EnforcementPoint.BEFORE_TOOL_EXECUTION
&& context.toolName == "account_delete") {
PolicyDecision.Deny(reason = "Account deletion is disabled", reasonCode = "account-delete-disabled")
} else {
baselinePolicy.evaluate(context)
}
}
Human Approval
A sensitive tool call can suspend execution, create a cryptographically bound approval challenge, and resume only when an external authorizer presents the valid token. The binding covers the workflow run ID, tool name, argument digest, policy version, and workflow digest — so an approval for one call cannot be replayed against a different call.
Audit and Evidence
Every policy decision is written to a SHA-256 hash-chained audit stream. Runtime evidence exporters produce structured artifacts (policy.decision, tool.permission) and evidence packs summarize deployment posture for reviewers — all inside the same runtime.
Policy Is Not Observability
Observability tells you what happened. Governance tells you what was allowed to happen. Both matter, and they are different problems:
- Logs and traces tell you a tool call succeeded or failed.
- Governance records the decision that permitted, denied, or queued that call — and who approved it.
TramAI does both, but the governance boundary is the part most JVM teams are missing. See runtime governance for the full distinction.
Java and Kotlin, First-Class
The enforcement machinery is Kotlin-first, Java-friendly, and framework-agnostic in its core:
- Kotlin:
SovereignTramai.builder()with a fluent, validated builder. - Java: the same contracts are reachable from Java through the standalone API.
- Spring Boot: the sovereign starter auto-configures the runtime, collects
TramaiToolbeans, and integrates with the application context.
When Not to Use This
In-process governance is not always the right answer:
- Prototyping and scripting — use the plain
Tramai.builder()fromtramai-standalonewithout the sovereign profile. Don't pay the fail-fast validation cost until you need it. - Fully unmanaged experimentation — if there is no policy to enforce, there is nothing for the governance boundary to do.
- Systems where the AI workload never touches sensitive data or tools — read-only summarization of public data may not need approval gates or trust zones.
The tradeoff is real: governance adds configuration and validation. It pays off when the workload can write data, call tools, or handle regulated data.
Related Documentation
- AI agent governance — the category pillar
- Runtime governance — enforcement points and the enforcement chain
- Tool governance — ALLOW / DENY / REQUIRE_APPROVAL in depth
- Human approval — the approval lifecycle
- Sovereign Mode — the composed runtime profile
- Sovereignty overview — what sovereignty means operationally
- Spring Boot guide — Spring integration
- TramAI on GitHub
