Sovereign Runtime Quickstart
The sovereign runtime is a runtime profile of the unified TramAI Spring Boot starter, not a separate starter. You add one starter, select tramai.profile: sovereign in configuration, and declare an explicit allowlist block. The same application code (@AiService interfaces, constructor injection, @AiTool methods) runs unchanged under either profile.
This page gets you to a startable sovereign application. For the security model behind it, see Sovereign Mode.
Maturity: the sovereign modules (dev.tramai:tramai-sovereign, tramai-spring-sovereign) are published preview surface in 0.6.0. Preview means the API can change in a minor release — 0.6.0 is not a stable 1.0 surface.
Add Dependencies
BOM-first, as with every other TramAI module:
dependencies {
implementation(platform("dev.tramai:tramai-bom:0.6.0"))
implementation("dev.tramai:tramai-spring-boot-starter")
implementation("dev.tramai:tramai-spring-provider-ollama") // or -openai / -anthropic
}
tramai-spring-boot-starter is an auto-configuration aggregator over tramai-spring-core (standard runtime) and tramai-spring-sovereign (sovereign runtime). The profile decides which one activates.
Sovereign subsystems are opt-in add-ons — add the ones matching the configuration you enable:
| Add-on | What it activates |
|---|---|
tramai-spring-boot-starter-sovereign-persistence-file | Encrypted file-backed stores (approvals, continuations, audit, outbox) |
tramai-spring-boot-starter-sovereign-persistence-jdbc | JDBC-backed stores, versioned schema migrations, worker leases, resume credentials |
tramai-spring-boot-starter-sovereign-ops | Audit outbox, recovery, dispatch, background workers, observer SPI |
tramai-spring-boot-starter-sovereign-ops-actuator | Read-only worker status endpoint and health component |
tramai-spring-boot-starter-sovereign-ops-micrometer | Micrometer metrics for the ops workers |
tramai-spring-boot-starter-sovereign-ops-observability | OpenTelemetry metrics for the ops workers |
tramai-spring-boot-starter-sovereign-ops-rest | Approval inbox and decision/resume REST control plane (preview) |
Secrets resolve through the same mechanism as any other Spring property: tramai-spring-secrets-file, tramai-spring-secrets-vault, tramai-spring-secrets-aws.
The former standalone sovereign starter coordinate (tramai-spring-boot-starter-sovereign) no longer exists in 0.6.0. If you are migrating, replace it with the unified starter plus tramai.profile — see Migrating to 0.6.0.
Module reference: tramai-spring-boot-starter, tramai-spring-sovereign, tramai-sovereign, tramai-persistence-jdbc.
Minimal Configuration
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
Four things must be true, or startup fails:
tramai.profileissovereign(orstandard, which skips all of this).- The provider adapter is on the classpath and configured — for Ollama,
base-urlis required; without it the adapter registers no provider. - Every allowed provider has an explicit trust zone.
- Every allowed model has a route to an allowed provider.
@AiService
interface InvoiceService {
@Operation(model = "local-invoice-model")
fun analyse(input: InvoiceInput): InvoiceAnalysisResult
}
No @EnableTramai is needed — Spring Boot auto-configuration is sufficient. The annotation does not select a runtime profile in 0.6.0; tramai.profile is the sole authority.
The tramai.sovereign.* Block
Bound by SovereignTramaiProperties (tramai-spring-sovereign):
| Property | Required | Meaning |
|---|---|---|
allowed-models | Yes | Models whose invocation is permitted. Non-empty. Every entry must appear as a key in models. |
allowed-providers | Yes | Providers that may be used. Non-empty. Every entry must have a provider-zones entry. |
allowed-tools | No | Tools whose execution is permitted. |
allowed-permissions | No | Tool permissions that are granted. |
provider-zones | Yes | Explicit trust zone per allowed provider — LOCAL, EU_CLOUD, or GLOBAL_CLOUD. Keys must be a subset of allowed-providers. |
models | Yes | Maps each logical model name to its provider. Keys must be a subset of allowed-models; values must be allowed providers. |
enabled | No | Compatibility switch. Defaults to true. |
There are no defaults and no wildcards: an empty allowlist denies everything, and a missing route is a startup error rather than a runtime surprise.
Fail-fast validation rules
Validation runs while the Spring context is starting. Each failure names the exact problem:
| Rule | Failure |
|---|---|
enabled must not be false | tramai.sovereign.enabled=false is not supported: tramai.profile is the sole runtime selector |
allowed-models non-empty | tramai-sovereign-spring-missing-allowed-models |
allowed-providers non-empty | tramai-sovereign-spring-missing-allowed-providers |
Zone value must be a ProviderTrustZone | tramai-sovereign-spring-invalid-provider-zone |
| Every allowed provider needs a zone | tramai-sovereign-spring-provider-zone-missing |
| No zones for unknown providers | tramai-sovereign-spring-provider-zone-unknown-provider |
| Every allowed model needs a route | tramai-sovereign-spring-missing-model-route |
| No routes for unknown models | tramai-sovereign-spring-model-route-unknown-model |
| Route target must be an allowed provider | tramai-sovereign-spring-model-route-unknown-provider |
| Unsupported profile value | Unsupported tramai.profile '<value>'. Supported values: standard, sovereign. |
tramai.sovereign.enabled: false is rejected deliberately. Before 0.6.0 sovereign was a separate starter and the flag meant "sovereign features off"; now the flag has no coherent meaning because the profile is the selector. Remove the property, or set tramai.profile: standard to run the standard runtime.
Persistence and Ops
Encrypted file-backed stores:
implementation("dev.tramai:tramai-spring-boot-starter-sovereign-persistence-file")
tramai:
sovereign:
persistence:
type: file
base-dir: ./data/tramai-sovereign
encryption:
key-env: TRAMAI_SOVEREIGN_STORE_KEY
TRAMAI_SOVEREIGN_STORE_KEY must hold a base64-encoded 256-bit key.
JDBC-backed stores (multi-node safe, schema migrations required before startup):
implementation("dev.tramai:tramai-spring-boot-starter-sovereign-persistence-jdbc")
implementation("dev.tramai:tramai-spring-boot-starter-sovereign-ops")
runtimeOnly("org.postgresql:postgresql")
tramai:
sovereign:
persistence:
type: jdbc
jdbc:
claim-lease-duration: 5m
max-claim-limit: 500
encryption:
key-env: TRAMAI_SOVEREIGN_STORE_KEY
ops:
outbox:
worker:
enabled: true
lease-enabled: true
lease-name: sovereign-ops-audit-outbox-worker
worker-id: ${HOSTNAME:node-1}
approved-resume-worker:
enabled: true
spring:
datasource:
url: jdbc:postgresql://localhost:5432/tramai
username: ${DB_USER}
password: ${DB_PASSWORD}
JDBC stores sanitize driver failures. A constraint violation or SQL syntax error surfaces as IllegalStateException with redacted details — never as a raw java.sql.SQLException carrying your query text, table names, or connection string. Do not write catch blocks for java.sql.SQLException; catch IllegalStateException (or RuntimeException) instead. Coroutine CancellationException and TramAI domain exceptions propagate unwrapped.
Non-Spring Usage
The same profile is available embedded, without Spring: SovereignTramai.builder() in dev.tramai.sovereign.
import dev.tramai.security.ProviderTrustZone
import dev.tramai.security.audit.InMemoryAuditStore
import dev.tramai.security.model.InMemoryModelRegistry
import dev.tramai.sovereign.SovereignDeploymentMode
import dev.tramai.sovereign.SovereignProfileConfiguration
import dev.tramai.sovereign.SovereignTramai
val profile = SovereignProfileConfiguration(
allowedModels = setOf("llama3.2"),
allowedProviders = setOf("ollama"),
allowedTools = setOf("schedule-payment"),
allowedPermissions = setOf("payment.schedule"),
providerZones = mapOf("ollama" to ProviderTrustZone.LOCAL),
deploymentMode = SovereignDeploymentMode.STANDARD,
)
val tramai = SovereignTramai.builder()
.profile(profile)
.modelRegistry(InMemoryModelRegistry.builder().register(registeredModel).build())
.auditStore(InMemoryAuditStore())
.provider(ollamaProvider, name = "ollama", default = true)
.model("llama3.2", "ollama")
.build()
try {
val service = tramai.create(InvoiceService::class)
// ...
} finally {
tramai.close() // SovereignTramai : AutoCloseable — close the owned engine
}
SovereignProfileConfiguration validates in its constructor and rejects what the Spring properties reject plus a few extras:
- empty
allowedModels/allowedProviders - blank entries, or entries with surrounding whitespace
- wildcard entries
- a
providerZoneskey that is not inallowedProviders - an allowed provider without a zone
allowedFallbackProvidersthat is not a subset ofallowedProviders
SovereignDeploymentMode.OFFLINE additionally requires every registered provider, primary route, fallback route, and default provider to be LOCAL; violating configurations fail with offline-profile-non-local-provider-rejected (and the route-specific variants). See Offline Deployment.
To run approval-gated tools and resume suspended executions:
val runtime = tramai.runtime()
runtime.registerService(InvoiceService::class) // required after a restart, before resume
runtime.resumeApprovalTyped<InvoiceAssessment>(command)
runtime.close()
Verify the Runtime You Built
Generate a deployment evidence pack — deterministic JSON with no secrets, tokens, prompts, or filesystem paths:
import dev.tramai.sovereign.evidence.SovereignEvidencePackWriter
import java.nio.file.Path
val pack = tramai.evidencePack() // dev.tramai.sovereign.evidence.SovereignEvidencePackV1
SovereignEvidencePackWriter.write(pack, Path.of("build/sovereign-evidence/sovereign-evidence-pack-v1.json"))
The pack records the deployment mode, the allowed models and providers, the provider zones, and the artifact-verification receipts from build time. Optional subsections cover zero-egress probes, audit-chain validity, SBOM linkage, release-bundle digests, and CI attestation. See Evidence Packs.
What This Quickstart Does Not Cover
- Key rotation and secrets lifecycle
- Applying the JDBC schema migrations (required before starting against a real database)
- Infrastructure-level network isolation for offline deployments — firewall,
NetworkPolicy, sandbox, or a physical air gap - A stable 1.0 API — types annotated
@ExperimentalTramaiInternalApiand@ExperimentalProviderTransportApiare internal cross-module contracts and may change without notice
Next Steps
- Sovereign Mode — the embedded runtime, builder API, and security invariants
- Offline Deployment — local-only enforcement and zero-egress verification
- Evidence Packs — deployment attestation
- Approval Workflows — human-in-the-loop suspend and resume
- Production Hardening & Security — the 0.6.0 runtime hardening guarantees
