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
| Feature | How Sovereign Enables It |
|---|---|
| Policy enforcement | PolicyConfiguration.secure() deny-by-default is wired automatically. Every provider, model, tool, and permission must be explicitly allowed. |
| Model registry | ModelRegistry SPI is mandatory and always enabled (non-disableable). Builder validates every allowed model has a primary route. |
| Audit trail | AuditEngine with SHA-256 hash-chained events is wired automatically via AuditEnginePolicyDecisionAuditEmitter. |
| DLP | Optional .dlp(interceptor) — wires DlpInterceptor into the standalone builder with optional .dlpRedactionAudit(emitter). |
| Approval workflows | Optional .approvalGateCoordinator(coordinator) with lifecycle audit via AuditEngineApprovalLifecycleAuditEmitter. |
| Artifact verification | Optional .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 deployment | SovereignDeploymentMode.OFFLINE rejects any non-LOCAL provider at build time. |
Security Invariants
The sovereign runtime enforces these invariants:
checkNotNullfor required inputs —SovereignProfileConfiguration,ModelRegistry, andAuditStoreare all required; missing any one throwsIllegalStateExceptionat build time.- Init validation on config —
SovereignProfileConfiguration.initrejects blanks, wildcards, emptyallowedModels/allowedProviders, providers without trust zones, and fallback providers not inallowedProviders. .secure().copy()pattern —toPolicyConfiguration()starts fromPolicyConfiguration.secure()(deny-by-default) and overlays only explicitly allowed entries. No wildcard bypass.- Forced-enabled features —
ModelRegistrySettings(enabled = true)and classification-awareProviderRoutingConfiguration(enabled = true)are always set. BEFORE_WORKFLOW_RESUMEis allowed by policy — resume authorization is enforced by theApprovalGateCoordinator, not the policy engine.- One selector —
tramai.profiledecides the runtime.tramai.sovereign.enabled: falseis 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, orGLOBAL_CLOUDand enforce data-classification routing - Air-gapped deployments — use
SovereignDeploymentMode.OFFLINEto 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:
| Input | Required | Validated |
|---|---|---|
SovereignProfileConfiguration | Yes | Blanks, wildcards, missing trust zones, empty allowlists |
ModelRegistry | Yes | Every allowed model must have an approved model entry |
AuditStore | Yes | Hash-chained audit events emitted for every policy decision |
| At least one provider | Yes | Must be registered via .provider() |
| Provider trust zones | Yes | Every 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:
- Required inputs —
checkNotNullfor profile, modelRegistry, auditStore - Registered providers — at least one must be registered
- Cross-reference — every registered provider must be in
allowedProvidersand vice versa - Trust zones — every registered provider must have a zone in
providerZones - Model routes — every allowed model must have a primary route
- Primary route targets — every route targets a registered, allowed provider
- Fallback routes — source and target models are allowed, target provider is registered and in
allowedFallbackProviders - Default provider — registered and in
allowedProviders - Offline deployment — all providers/routes are
LOCAL(runs before registry lookup) - 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-rejectedartifact-approved-model-lookup-failedartifact-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:
| Zone | Meaning |
|---|---|
ProviderTrustZone.LOCAL | Same-host or isolated-network provider (Ollama, vLLM, llama.cpp) |
ProviderTrustZone.EU_CLOUD | Cloud provider hosted within the EU trust boundary |
ProviderTrustZone.GLOBAL_CLOUD | Global cloud provider outside EU jurisdiction |
Classification-aware routing is always enabled in sovereign mode via ProviderRoutingConfiguration.sovereignDefaults():
| Classification | Allowed Zones | Fallback Zones |
|---|---|---|
RESTRICTED | LOCAL only | None (no fallback) |
CONFIDENTIAL | LOCAL, EU_CLOUD | LOCAL, EU_CLOUD |
INTERNAL | Any zone | Any zone |
PUBLIC | Any zone | Any 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
sequenceNumberper audit stream previousEventHash— SHA-256 of the preceding event (hash-chained)enforcementPoint,decision,actor,reasonCodeeventHash— 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 proxyregisterService(serviceType)— register a service without creating a proxy (needed after runtime restart beforeresumeApproval)resumeApproval(command)— resume approval-suspended tool executionresumeApprovalTyped<R>(command)— typed overloadclose()— close the runtime
Limitations
- No nested approval in v1 —
ConfigurationExceptionis thrown if a tool execution inside an already-suspended workflow triggers another approval gate BEFORE_WORKFLOW_RESUMEis allowed by policy — resume authorization is delegated to theApprovalGateCoordinator, not the policy engine- Verification receipts are immutable — once built, the list cannot be modified
- Security profile is immutable —
SovereignProfileConfigurationis 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/@ExperimentalProviderTransportApiare internal cross-module contracts outside the compatibility promise
Next Steps
- Sovereign Runtime Quickstart — dependency, profile, allowlists, and startup validation
- DLP — add data loss prevention to your sovereign runtime
- Approval Workflows — add human-in-the-loop gates
- Artifact Verification — verify model integrity at build time
- Evidence Packs — generate deployment attestation evidence
- Offline Deployment — deploy in fully air-gapped environments
