TramAI - governed AI workflows for Java and Kotlin

Sovereign Mode

Sovereign Mode is a secure-by-default runtime profile that wires the policy engine, model registry, audit store, DLP, approval workflows, and artifact verification into one fail-fast runtime. You either select it in configuration (tramai.profile: sovereign) or assemble it directly with SovereignTramai.builder(). Either way the allowlists are validated before any model is invoked.

Modules: dev.tramai:tramai-sovereign (embedded runtime profile) and dev.tramai:tramai-spring-sovereign (Spring auto-configuration; pulled in by tramai-spring-boot-starter). Module cards: tramai-sovereign, tramai-spring-sovereign.

Spring applications should start with the Sovereign Runtime Quickstart — this page covers the runtime itself, the builder API, and the invariants both paths share.


What

SovereignTramai wraps the standalone Tramai runtime with mandatory security configuration. Every builder input is validated at build time — missing models, unregistered providers, missing trust zones, and cross-zone routing violations all throw before any runtime code executes.

The Spring profile (tramai.profile: sovereign) constructs the same thing from properties: tramai-spring-sovereign binds tramai.sovereign.* into SovereignProfileConfiguration, creates an in-memory model registry and default in-memory stores, and exposes the sovereign runtime as a bean.

What It Includes

FeatureHow Sovereign Enables It
Policy enforcementPolicyConfiguration.secure() deny-by-default is wired automatically. Every provider, model, tool, and permission must be explicitly allowed.
Model registryModelRegistry SPI is mandatory and always enabled (non-disableable). Builder validates every allowed model has a primary route.
Audit trailAuditEngine with SHA-256 hash-chained events is wired automatically via AuditEnginePolicyDecisionAuditEmitter.
DLPOptional .dlp(interceptor) — wires DlpInterceptor into the standalone builder with optional .dlpRedactionAudit(emitter).
Approval workflowsOptional .approvalGateCoordinator(coordinator) with lifecycle audit via AuditEngineApprovalLifecycleAuditEmitter.
Artifact verificationOptional .modelArtifactVerifier(verifier) — verified at build time; receipts accessible via verificationReceipts().
Evidence packs.evidencePack() generates deterministic SovereignEvidencePackV1 with audit chain, verification receipts, and optional sub-sections.
Offline deploymentSovereignDeploymentMode.OFFLINE rejects any non-LOCAL provider at build time.

Security Invariants

The sovereign runtime enforces these invariants:

  1. checkNotNull for required inputs — SovereignProfileConfiguration, ModelRegistry, and AuditStore are all required; missing any one throws IllegalStateException at build time.
  2. Init validation on config — SovereignProfileConfiguration.init rejects blanks, wildcards, empty allowedModels/allowedProviders, providers without trust zones, and fallback providers not in allowedProviders.
  3. .secure().copy() pattern — toPolicyConfiguration() starts from PolicyConfiguration.secure() (deny-by-default) and overlays only explicitly allowed entries. No wildcard bypass.
  4. Forced-enabled features — ModelRegistrySettings(enabled = true) and classification-aware ProviderRoutingConfiguration(enabled = true) are always set.
  5. BEFORE_WORKFLOW_RESUME is allowed by policy — resume authorization is enforced by the ApprovalGateCoordinator, not the policy engine.
  6. One selector — tramai.profile decides the runtime. tramai.sovereign.enabled: false is rejected at startup; there is no second switch.

When to Use

Use Sovereign Mode when you need:

  • A hard security boundary — every model, provider, tool, and permission must be explicitly allowed in the profile
  • Fail-fast configuration — provider/route mismatches are caught at startup or build(), not at model invocation time
  • Audit-grade policy decisions — every allow/deny is recorded in a SHA-256 hash-chained event stream
  • Trust-zone enforcement — classify each provider as LOCAL, EU_CLOUD, or GLOBAL_CLOUD and enforce data-classification routing
  • Air-gapped deployments — use SovereignDeploymentMode.OFFLINE to reject any non-local provider
  • Multi-tenant platforms — each tenant gets its own isolated sovereign runtime with independent allowlists

Do not use Sovereign Mode for prototyping or scripting — use the standard profile (the default) instead.


Spring Boot Quickstart

The full walkthrough, including the add-on matrix and fail-fast error codes, is in the Sovereign Runtime Quickstart. The short version:

dependencies {
    implementation(platform("dev.tramai:tramai-bom:0.6.0"))
    implementation("dev.tramai:tramai-spring-boot-starter")
    implementation("dev.tramai:tramai-spring-provider-ollama")
}
tramai:
  profile: sovereign
  providers:
    ollama:
      base-url: http://localhost:11434
  sovereign:
    allowed-models: [local-invoice-model]
    allowed-providers: [ollama]
    provider-zones:
      ollama: LOCAL
    models:
      local-invoice-model: ollama

@AiService interfaces are exposed as beans exactly as they are under the standard profile; no @EnableTramai step is required.


Embedded Quickstart

Without Spring:

// 1. Define the sovereign profile
val profile = SovereignProfileConfiguration(
    allowedModels = setOf("llama3.2"),
    allowedProviders = setOf("ollama"),
    allowedFallbackProviders = emptySet(),
    allowedTools = emptySet(),
    allowedPermissions = emptySet(),
    providerZones = mapOf("ollama" to ProviderTrustZone.LOCAL),
    deploymentMode = SovereignDeploymentMode.STANDARD,
)

// 2. Build the sovereign runtime
val tramai = SovereignTramai.builder()
    .profile(profile)
    .modelRegistry(modelRegistry)
    .auditStore(auditStore)
    .provider(ollamaProvider, name = "ollama", default = true)
    .model("llama3.2", "ollama")
    .build()

// 3. Use like a normal Tramai instance, and close it
val service = tramai.create<MyService>()
tramai.close()

Builder Requirements

The builder enforces these inputs at build time:

InputRequiredValidated
SovereignProfileConfigurationYesBlanks, wildcards, missing trust zones, empty allowlists
ModelRegistryYesEvery allowed model must have an approved model entry
AuditStoreYesHash-chained audit events emitted for every policy decision
At least one providerYesMust be registered via .provider()
Provider trust zonesYesEvery allowed provider must have an entry in profile.providerZones

Builder API Reference

Required Methods

fun profile(configuration: SovereignProfileConfiguration): Builder
fun modelRegistry(registry: ModelRegistry): Builder
fun auditStore(store: AuditStore): Builder
fun provider(
    provider: ModelProvider,
    name: String = provider.providerId(),
    default: Boolean = false,
): Builder
fun model(modelName: String, providerName: String): Builder

Provider Registration

.provider() delegates to the standalone builder. Routing validation (blank or whitespace names, duplicates, unknown providers, invalid defaults) is deferred to build(), which throws ConfigurationException for invalid routing.

Model Registration

The .model() method maps a logical model name to a registered provider. Every model in allowedModels must have a primary route.

Fallback Routes

fun fallbackModel(
    requestedModelName: String,
    fallbackModelName: String,
    providerName: String,
): Builder

fun fallbackProvider(
    modelName: String,
    providerName: String,
): Builder  // convenience: fallbackModel with same model name

Fallback providers must be in allowedFallbackProviders, which must be a subset of allowedProviders.

Default Provider

fun defaultProvider(providerName: String): Builder

Overrides the default flag on a previously registered provider. Must be in allowedProviders.

Optional Features

.dlp(interceptor)                                          // DLP
.dlpRedactionAudit(emitter)                                // DLP audit
.approvalGateCoordinator(coordinator)                      // Approval workflows
.approvalContinuationStore(continuationStore)              // Approval persistence
.suspendedInvocationStore(invocationStore)                 // Suspension persistence
.toolArgumentsDigester(digester)                           // Approval argument hashing
.modelArtifactVerifier(verifier)                           // Artifact verification
.modelArtifactVerificationSettings(settings)               // Verification settings
.cache(cache)                                              // Response caching
.circuitBreaker(settings)                                  // Resilience
.retryPolicy(settings)                                     // Retry policy
.tokenBudget(settings)                                     // Token budget
.observer(observer)                                        // Observability
.interceptor(interceptor)                                  // Operation interceptors
.engineEventObserver(eventObserver)                        // Engine events
.toolResultFiltering(settings)                             // Tool result filtering
.clock(clock)                                              // Custom clock

Build-Time Validation

The sovereign builder performs fail-fast validation before any runtime code runs. Validation order:

  1. Required inputs — checkNotNull for profile, modelRegistry, auditStore
  2. Registered providers — at least one must be registered
  3. Cross-reference — every registered provider must be in allowedProviders and vice versa
  4. Trust zones — every registered provider must have a zone in providerZones
  5. Model routes — every allowed model must have a primary route
  6. Primary route targets — every route targets a registered, allowed provider
  7. Fallback routes — source and target models are allowed, target provider is registered and in allowedFallbackProviders
  8. Default provider — registered and in allowedProviders
  9. Offline deployment — all providers/routes are LOCAL (runs before registry lookup)
  10. Artifact verification — manifests are resolved and verified against local model files

Error messages use safe-code identifiers for deterministic integration testing:

  • offline-profile-non-local-provider-rejected
  • artifact-approved-model-lookup-failed
  • artifact-digest-required-for-local-model

The Spring property layer adds its own codes for the same rules — tramai-sovereign-spring-missing-allowed-models, tramai-sovereign-spring-provider-zone-missing, tramai-sovereign-spring-missing-model-route, and friends. They are listed in the quickstart.


Provider Trust Zones

Each provider is classified into a trust zone. The zones are enforced by the policy engine during provider routing:

ZoneMeaning
ProviderTrustZone.LOCALSame-host or isolated-network provider (Ollama, vLLM, llama.cpp)
ProviderTrustZone.EU_CLOUDCloud provider hosted within the EU trust boundary
ProviderTrustZone.GLOBAL_CLOUDGlobal cloud provider outside EU jurisdiction

Classification-aware routing is always enabled in sovereign mode via ProviderRoutingConfiguration.sovereignDefaults():

ClassificationAllowed ZonesFallback Zones
RESTRICTEDLOCAL onlyNone (no fallback)
CONFIDENTIALLOCAL, EU_CLOUDLOCAL, EU_CLOUD
INTERNALAny zoneAny zone
PUBLICAny zoneAny zone

Hash-Chained Audit

The AuditEngine is wired automatically and records every policy decision:

val auditEng = AuditEngine(store = auditStore, clock = clock)
val policyAuditEmitter = AuditEnginePolicyDecisionAuditEmitter(auditEng)
val approvalLifecycleEmitter = AuditEngineApprovalLifecycleAuditEmitter(auditEng)

Each AuditEvent contains:

  • Sequential sequenceNumber per audit stream
  • previousEventHash — SHA-256 of the preceding event (hash-chained)
  • enforcementPoint, decision, actor, reasonCode
  • eventHash — SHA-256 of the full event (self-hash)
  • ISO-8601 timestamp

The BEFORE_WORKFLOW_RESUME enforcement point is intentionally allowed by the sovereign policy engine — resume authorization is enforced by the ApprovalGateCoordinator, which validates token binding and expected-version checks before the workflow can resume.

Tool enforcement points (BEFORE_TOOL_EXPOSURE, BEFORE_TOOL_EXECUTION, BEFORE_TOOL_RESULT_REINJECTION) are partitioned into the dedicated tool.permission runtime evidence family — see Evidence Packs.


Verification Receipts

After building, SovereignTramai.verificationReceipts() returns an immutable list of VerifiedLocalModelArtifact entries — one per local-model route that was verified at build time. Artifact verification covers both primary AND fallback routes.

val tramai = SovereignTramai.builder()
    .profile(profile)
    .modelRegistry(registry)
    .auditStore(store)
    .provider(ollamaProvider, name = "ollama", default = true)
    .model("llama3.2", "ollama")
    .modelArtifactVerifier(verifier)
    .modelArtifactVerificationSettings(
        ModelArtifactVerificationSettings(enabled = true)
    )
    .build()

tramai.verificationReceipts().forEach { receipt ->
    println("${receipt.modelName}: ${receipt.manifestDigest}")
}

SovereignTramaiRuntime

For longer-lived sessions with approval-resume support:

val runtime = tramai.runtime()

// Register services needed for resume
runtime.registerService<InvoiceIntelligenceService>()

// Resume an approval-suspended tool execution
val result = runtime.resumeApprovalTyped<InvoiceAssessment>(command)

runtime.close()

SovereignTramaiRuntime wraps the standalone runtime so unsafe standalone methods do not leak into the sovereign API. It exposes:

  • create(serviceType) — create a service proxy
  • registerService(serviceType) — register a service without creating a proxy (needed after runtime restart before resumeApproval)
  • resumeApproval(command) — resume approval-suspended tool execution
  • resumeApprovalTyped<R>(command) — typed overload
  • close() — close the runtime

Limitations

  • No nested approval in v1 — ConfigurationException is thrown if a tool execution inside an already-suspended workflow triggers another approval gate
  • BEFORE_WORKFLOW_RESUME is allowed by policy — resume authorization is delegated to the ApprovalGateCoordinator, not the policy engine
  • Verification receipts are immutable — once built, the list cannot be modified
  • Security profile is immutable — SovereignProfileConfiguration is a data class; changes require rebuilding
  • Evidence packs are snapshot-only — they capture a point-in-time view of deployment state
  • Preview API — the sovereign modules are published preview surface in 0.6.0; symbols marked @ExperimentalTramaiInternalApi / @ExperimentalProviderTransportApi are internal cross-module contracts outside the compatibility promise

Next Steps