Configuration

All properties bind under the cortex prefix. ontologies, shapes, and rules each accept a list of classpath:/file: resources, merged in order.

Core

PropertyDefaultDescription
cortex.persistentfalsePersist approved assertions to a TDB2 store on disk. When false, held in an in-memory TDB2 dataset. Either way the inference closure and full-text index are always an in-memory cache, rebuilt on every startup.
cortex.assertionsLocation.cortex/dbDirectory of the TDB2 store, used only when cortex.persistent=true.
cortex.ontologiesclasspath:ontology.ttlThe ontologies the graph is built on (Turtle), merged in order.
cortex.shapesclasspath:ontology.shapesThe SHACL shapes ingested assertions are validated against, merged in order.
cortex.rulesclasspath:ontology.rulesThe Jena rules used for inference, concatenated in order.
cortex.search.viewHalfLife30dHow long it takes a view's contribution to search ranking to halve. Results are ranked by textual relevance, then weighted by how often each resource has been deliberately opened. Set 0 to disable decay and weight by the raw lifetime count instead. The weight is bounded either way, so popularity reorders comparable results rather than overriding relevance.
cortex.web.enabledtrueRegister the web UI controllers. Set false for the graph without the UI.
cortex.mcp.enabledtrueRegister the MCP tools and resources. Set false for the graph without an MCP server.
Single-JVM constraint. When cortex.persistent=true, the TDB2 store is opened with TDB2Factory.connectDataset, which does not support two JVMs opening the same directory. Run one instance per persistent store directory.

S3 client

PropertyDefaultDescription
cortex.s3.enabledfalseRegister the S3Client bean. Independent of backup and restore, though both require it.
cortex.s3.endpointunsetThe S3 endpoint, e.g. http://localhost:9000 for MinIO. When unset, the AWS endpoint for the region is used.
cortex.s3.bucketunsetThe bucket backups are uploaded to. Required when backups are enabled.
cortex.s3.regionus-east-1The region to sign for. Required by the SDK even against S3-compatible stores that ignore it.
cortex.s3.authdefaultdefault (AWS credential chain), static (the key/secret below), or anonymous (no credentials).
cortex.s3.accessKeyIdunsetAccess key. Required when auth=static.
cortex.s3.secretAccessKeyunsetSecret key. Required when auth=static.
cortex.s3.pathStyleAccessfalseAddress buckets as a path segment (endpoint/bucket/key) rather than a subdomain. MinIO and most S3-compatible stores need this.
cortex.s3.proxy.endpointunsetHTTP proxy to reach S3 through. Setting it is what enables the proxy — there is no separate flag.
cortex.s3.proxy.username / passwordunsetProxy credentials, when it requires authentication.
cortex.s3.proxy.nonProxyHostsunsetHosts to reach directly, bypassing the proxy.
cortex.s3.proxy.useSystemPropertyValuesfalseFall back to the http(s).proxyHost JVM properties. The AWS SDK defaults this to true; Cortex does not.
cortex.s3.proxy.useEnvironmentVariableValuesfalseFall back to HTTP_PROXY/HTTPS_PROXY/NO_PROXY. The AWS SDK defaults this to true; Cortex does not.
An unset proxy is genuinely no proxy. The AWS SDK resolves proxy settings from JVM system properties and environment variables by default, so an ambient HTTPS_PROXY would otherwise silently route uploads through a proxy named nowhere in cortex.s3.*. Cortex defaults both discovery flags to false. Set them to true if you want the SDK's ambient discovery.

Scheduled backups

A Quartz job periodically snapshots the whole assertions dataset — approved assertions, every staged branch, and the provenance graph — via TDB2's own DatabaseMgr.backup (gzipped N-Quads, taken inside a read transaction, so it does not lock out ingestion or approval) and uploads it to S3. This is the real backup mechanism; /export is not.

PropertyDefaultDescription
cortex.backup.enabledfalseRegister the scheduled backup job.
cortex.backup.interval24hHow often a backup is taken. The first runs one interval after startup, not at startup.
cortex.backup.keyPrefixcortex/Prepended to the backup file name to form the S3 object key. Include a trailing / to group under a folder.
cortex.backup.onShutdowntrueTake a final backup as the application shuts down, blocking shutdown until it has uploaded. Applies only when backups are enabled.

Backups are off by default, so the starter does not bring their dependencies. Add:

Gradle
implementation(platform("software.amazon.awssdk:bom:2.46.7"))
implementation("software.amazon.awssdk:s3")
implementation("software.amazon.awssdk:apache-client")
implementation("org.springframework.boot:spring-boot-starter-quartz")

apache-client is not optional: the s3 artifact ships only the async Netty HTTP client, so without it the synchronous S3Client fails to build. Then configure it — this targets a local MinIO with no authentication:

application.yaml
cortex:
  persistent: true
  backup:
    enabled: true
    interval: 6h
  s3:
    enabled: true
    endpoint: http://localhost:9000
    bucket: cortex-backups
    region: us-east-1
    auth: anonymous
    path-style-access: true
Backups require cortex.persistent=true. A TDB2 backup is an admin operation on an on-disk store; enabling backups without persistence — or with a missing bucket, missing static credentials, a non-positive interval, or the dependencies above — fails at startup rather than booting an app that reports healthy and never backs anything up.

Local backup files accumulate under <cortex.assertionsLocation>/Backups/ and are never deleted — the upload is a copy, not a move, so a bucket misconfiguration can never destroy data. Pruning is left to the volume, or to an S3 lifecycle rule.

Backup on shutdown

Backups are scheduled, so a restart would otherwise discard every approval made since the last one — up to a full cortex.backup.interval, 24 hours by default. Cortex therefore takes one final backup as the application shuts down and blocks shutdown until it has uploaded. It is on whenever backups are enabled; set cortex.backup.onShutdown=false to turn it off without giving up scheduled backups.

The final backup runs after the web server has stopped accepting requests and after the scheduler has quiesced — including waiting out a scheduled backup that happens to be in flight — so it snapshots a store nothing is still writing to. It is an ordinary backup under the same key prefix, so a restore picks it up as the most recent object with no extra configuration.

The wait is deliberately unbounded. A large store on a slow link takes as long as it takes, and a bounded wait would kill the upload partway through on exactly the stores that most need it. A backup that fails — bad credentials, a missing bucket — is logged and shutdown continues, so a misconfiguration can never make the application impossible to restart; only a genuinely slow upload holds shutdown open. If your orchestrator sends SIGKILL after a grace period, size that period against how long a backup of your store actually takes.

Restore on startup

The inverse of a backup: on boot, download the most recent backup from S3 and load it into the store before the application serves traffic, so an instance whose local disk was replaced comes up with its data. The inference closure and index rebuild from the restored assertions as usual.

PropertyDefaultDescription
cortex.restore.enabledfalseDownload the latest backup under cortex.restore.keyPrefix and load it at startup.
cortex.restore.keyPrefixcortex/The S3 key prefix to look for backups under. Set it to match cortex.backup.keyPrefix if you customize that.

Restore needs software.amazon.awssdk:s3 and apache-client plus an enabled S3 client (Quartz is not needed — restore uses no scheduler):

application.yaml
cortex:
  persistent: true
  restore:
    enabled: true
  s3:
    enabled: true
    endpoint: http://localhost:9000
    bucket: cortex-backups
    region: us-east-1
    auth: anonymous
    path-style-access: true
A restore is destructive by design. It is a wipe-and-load that runs on every boot — it clears the local store and loads the latest backup over it, restoring approved assertions, staged branches, and provenance at full fidelity. Any local writes not yet captured by a backup are lost on the next boot — which is most of what backup on shutdown is for, since it closes that window for every clean stop, leaving only a crash or a SIGKILL able to lose work. An empty bucket (a first-ever deploy) is not an error — it logs and starts empty; every other misconfiguration fails the context. Leave it false if S3 is not your source of truth.

The switch is independent of cortex.backup.enabled: an instance may restore from another's uploads without scheduling backups of its own, or do both.