Open-source agentic AI for transaction monitoring. Just ask Jooby.What's Jube? →

Environment Variables

In Jube, application settings can only be passed via operating system (or container) Environment Variables. The following hardcoded Environment Variables, this would be to say defaults, are available:

Variable Default Value Description
ModelSynchronisationWait 10000 The duration in milliseconds between model synchronisation. Many of the entities in model synchronisation require explicit instruction to synchronise, with that instruction being checked for each interval. Model entities which do not require explicit synchronisation will be included for each interval.
EnableNotification True A boolean value to indicate if Notifications are enabled in the instance, in which case background threads will be established to relay the notifications created by the model invocation, and that model invocation may be branched to dispatch notifications to the thread.
EnableTtlCounter True A boolean value indicating if this instance should start a thread to manage the decrement of TTL Counters.
ConnectionString null The connection string to the Postgres database.
CacheConnectionString null The connection string to the optional Cache Postgres database. In the absence of value will fall back to ConnectionString.
MigrationConnectionString null An optional, separate connection string used only for running Migration on startup (see Architecture), falling back to ConnectionString - with a warning logged - when unset. Useful to grant DDL permissions (CREATE TABLE, CREATE INDEX) only to the credential used for Migration, while ConnectionString itself stays scoped to a restricted, DDL-less runtime user - see the restricted Postgres users note on Deploying with Docker.
ReportConnectionString null The connection string to the Reporting Postgres database. In the absence of value there is no fallback value. In practice the connection string might be the same as the main, but be a different account for the purpose of reporting. When set, reprocessing and backtests also read the archive through it, so a read replica can take that load.
EnableSearchKeyCache True A boolean value indicating if this instance should start a thread to manage the computation of Abstraction Rules set to Cache, meaning the background computation of aggregation values. Only a single instance may exist.
EnableCasesAutomation True A boolean value indicating if this instance should start a thread to manage changing of status in the case records. Only a single instance may exist.
CasesAutomationWait 60000 Subject to EnableCasesAutomation being True, the duration in milliseconds between model case status change and automation.
EnableEntityModel True A boolean value to indicate if the models should be synchronised to this instance thus being available for invocation.
ArchiverPersistThreads 4 The number of threads preparing records for bulk insert to the Archive and ArchiveKey tables. In extremely high throughput implementations with thousands of records being inserted, balancing the number of threads inserting records with the amount of records batched to insert allows for write performance optimisation. Zero (0) has the effect of disabling.
ModelInvokeAsynchronousThreads 1 The number of threads allocated to processing model invocation switched to be asynchronous. Zero (0) has the effect of disabling.
BulkCopyThreshold 1000 For each Archiver Persist Thread the number of records to be buffered before a bulk copy is committed. A maximum of ten seconds is allowed from the last message else bulk insert is performed regardless.
ActivationWatcherAllowPersist True A boolean value to indicate that the instance may persist activation records to the ActivationWatcher table and also allowing for the instantiation of threads to manage the asynchronous bulk insert.
ActivationWatcherPersistThreads 1 The number of threads preparing records for bulk insert into the ActivationWatcher table. Zero (0) has the effect of disabling.
ActivationWatcherBulkCopyThreshold 100 For each Activation Watcher Persist Thread the number of records to be buffered before a bulk copy is committed.
EnableReprocessing True A boolean value to indicate that the reprocessing of model invocation given Archive data, be allowed to create threads.
ReprocessingThreads 1 The number of threads allocated to reprocessing model invocation given Archive data. Zero (0) has the effect of disabling.
ReprocessingBulkLimit 10000 Reprocessing reads the Postgres Archive a page at a time, oldest reference date first, invokes the model for each matched record and writes the page’s updated archive records together in one transaction. The value is the number of records in a page, which bounds the instance memory and sets how many archive updates share a transaction.
ThreadPoolManualControl False A boolean value to override the .Net Thread Pool with MinThreadPoolThreads and MaxThreadPoolThreads values, else .Net self managed. Managing the thread pool manually can reduce apparent warm up time for the application and reduce risk of thread starvation through threads being closely sized to underlying compute resources. All requests to the Kestrel web server will place demand on the Thread Pool, with each IO demand being made ashncronously (and likely in a seperate thread allocated by the Thread Pool). Model invocation can be decoupled from the thread pool partially via the use of asynchronous model invocation, otherwise it forms part of the request handled by the Kestrel thread.
MinThreadPoolThreads 30 Given ThreadPoolManualControl being True, the minimum threads in the Thread Pool.
MaxThreadPoolThreads 1000 Given ThreadPoolManualControl being True, the maximum threads in the Thread Pool.
MaximumModelInvokeAsyncQueue 10000 The maximum number of pending asynchronous HTTP Model invocations allowed in queue before further requests are returned with an error.
SMTPHost null For notification functionality, the SMTP server host.
SMTPPort 587 The SMTP server port.
SMTPUser null Assuming authentication the SMTP server user name.
SMTPPassword null The SMTP server password given user name.
SMTPFrom null The email address to be sent from.
ClickatellAPIKey null For notification functionality, the API key provided by Clickatell for the SMS dispatch service.
HttpAdaptationUrl http://localhost:5001 As a security measure a prefix to the HTTP Endpoint value entered by the end user in HTTP Adaptation to recall scores.
HttpAdaptationTimeout 1000 The timeout value for HTTP Adaptation endpoint recall.
HttpAdaptationValidateSsl False A boolean value to ensure SSL validation for HTTP Adaptation endpoint recall.
EnableSanction True A boolean value to indicate if sanctions should be loaded from the database, cached to the instance memory and made available for searching in model invocation or directly.
EnableSanctionLoader False A boolean value to indicate that this instance should load the Sanctions tables from local or internet resources. Disabled by default for reasons of security and privacy given outbound request to external HTTP resources.
EnableMockEndpoints True A boolean value to indicate if the unauthenticated mock endpoints are mapped: the HTTP Adaptation Protocol test doubles under /api/MockHttpAdaptation/* (see HTTP Adaptation Protocol) and the mock RSA MFA verifier at /api/Mfa. Enabled by default, because the seeded example model points its adaptation at one of them and the endpoints are the documented way to exercise the protocol without an R or Python sandbox. Set to False on any deployment that does not need them: they are anonymous by design and one of them, ServerError, returns a 500 deliberately, so a security scan of an instance that leaves them mapped will report that 500 as a server error and an application error disclosure. The nightly OWASP ZAP scan sets this to False for exactly that reason.
EnableVisualisationDatasourceTableAllowList True A boolean value to indicate if the SQL command of a Visualisation Registry Datasource is validated and executed through the same closed table allow-list and tenancy-scoped table rewriting as the conversational agent’s dynamic SQL, rather than only the protected-table deny list. When True, which is the default, every table referenced by a datasource command must be registered in the dynamic SQL table registry and each reference is replaced at execution with a subquery filtering rows to the caller’s tenant and model roles, so a command can never return another tenant’s rows; a command referencing an unregistered table is rejected both when the datasource is saved and when it is executed. Set to False to restore the earlier behaviour, in which any table outside the protected deny list may be referenced without row-level filtering: appropriate only for single-tenant deployments whose datasources rely on tables that are not registered.
ApprovalsRequired 1 The number of distinct approvers that must approve a version of a configuration entity before the engine will load it - see Maker Checker. One (1) means a single checker, which is the default; two (2) gives four-eyes approval with no schema change, because approvals are rows keyed on the entity version rather than a flag on the record. An approver may only count once towards the threshold, and the maker of a version may never approve it, so a value above the number of people who can approve will stall every change.
EnableDynamicEval False A boolean value to indicate if named dynamic expressions (rows in the DictionaryEvalExpression table) are made available as rule extension methods - see Curated Dynamic Expressions. Disabled by default for reasons of security: when False, no curated names are registered as rule tokens and the runtime evaluation call refuses to run. The underlying mechanism is not directly reachable from rule text regardless of this switch - only the automatic per-name rewrite can reach it.
EnableInlineScriptExecution False A boolean value to indicate if the EntityAnalysisModelInlineScriptExecute agent tool may run an inline script against an invocation context outside the engine. Disabled by default for reasons of security: inline scripts are whole VB.NET or C# classes that are not restricted by the rule token allow-list and receive the invocation context, so running one could call out or have side effects. When False the tool refuses without compiling anything. Rule execution tools are not affected.
EnableBacktest True A boolean value to start the backtest threads, which run backtests submitted from the rule pages and the backtest tools (EntityAnalysisModelBacktestSubmit). When False submitted backtests stay Pending until a node with it True picks them up. The immediate backtest (EntityAnalysisModelBacktestRunRule) is not affected.
BacktestThreads 1 The number of threads on this node that claim and run submitted backtests, one instance per thread at a time. Several nodes and threads share the queue safely. Zero (0) has the effect of disabling.
BacktestMaxRows 1000000 The most archived transactions one submitted backtest may read. Backtests read the archive in pages and evaluate in memory, so millions of rows are practical; the cost is one indexed range read per page.
BacktestOnlineMaxRows 10000 The most archived transactions the immediate backtest (EntityAnalysisModelBacktestRunRule) may read while the caller waits. Larger backtests must be submitted.
BacktestPageSize 5000 How many archived transactions a backtest reads from the database at a time. Only one page is held in memory; progress and stop requests are checked between pages.
BacktestMaxRunInterval s The unit for BacktestMaxRunIntervalValue: s (seconds), n (minutes), h (hours) - any other value, including d, is treated as days.
BacktestMaxRunIntervalValue 3600 The longest a submitted backtest may run, in the unit set by BacktestMaxRunInterval, before it is stopped and marked Failed.
BacktestStaleInterval s The unit for BacktestStaleIntervalValue: s (seconds), n (minutes), h (hours) - any other value, including d, is treated as days.
BacktestStaleIntervalValue 120 A submitted backtest left Running whose heartbeat is older than this (in the unit set by BacktestStaleInterval) is marked Failed, for example after an engine stopped mid run. The heartbeat is sent on a timer, every quarter of this value and at most every 10 seconds, so a long page does not count as stalled. It is never run again automatically.
BacktestMaxConcurrentRunsPerTenant 1 How many submitted backtests of one tenant may run at once across all nodes, so one tenant cannot hold every backtest thread; tenants waiting are served in turn.
SanctionLoaderWait 3600000 The duration in milliseconds between polling the file system or internet location for sanctions files. Raised from an earlier 60000 (one minute) default after that interval hit rate limits against a real sanctions source - now polled hourly by default.
EnableSanctionLoaderChangePoll True True (the default) checks the sanctions import audit at SanctionLoaderChangePoll intervals and refreshes the sanctions cache when a new import is seen. False restores a single wait of SanctionLoaderWait between refreshes. It does not affect EnableSanctionLoader, which only gates the loading of HTTP and directory sources.
SanctionLoaderChangePoll 60000 The interval in milliseconds at which the engine checks the sanctions import audit table (SanctionEntryImport) for a new import, for example an upload from the UI, and when one is found refreshes the in-memory sanctions cache at once instead of waiting for SanctionLoaderWait. Entries removed by the import leave the cache too. A missing, non numeric, zero or negative value is read as 60000, and a value at or above SanctionLoaderWait means a single check at the end of the wait. Switch the check off with EnableSanctionLoaderChangePoll.
NegotiateAuthentication False A boolean value to indicate that authentication should take place via Negotiated Authentication (commonly known as Active Directory in Windows networks) rather than JSON Web Token authentication. Very common in corporate environments.
ExhaustiveDatasetUploadMaxBytes 52428800 The maximum size in bytes of a dataset file uploaded for Exhaustive training. Uploads larger than this are rejected while streaming, before anything is stored. Also sets the request body limit of the upload endpoint (plus a small allowance for the multipart envelope).
ExhaustiveDatasetUploadMaxRows 1000000 The maximum number of data rows (excluding the header) of a dataset file uploaded for Exhaustive training. A file with more rows is rejected and nothing is stored.
ExhaustiveDatasetUploadMaxColumns 500 The maximum number of columns of a dataset file uploaded for Exhaustive training. A file with more columns is rejected and nothing is stored.
AMQP False A boolean value to indicate that connections should be established via the AMQPUri connection string to the RabbitMQ server or cluster.
AMQPUri null The connection string to the RabbitMQ server or cluster.
JWTValidAudience http://localhost:5001 The server domain that is allowed for the token.
JWTValidIssuer http://localhost:5001 The server domain that issued the token.
JWTKey null The encryption key for the JWT. This key is created randomly on first startup of the Jube instance, but can be changed. The key value is stored in the Jube.environment file, having being randomly created on application first use.
PasswordHashingKey null Passwords are encrypted in an irreversible manner using the Argon2 hashing algorithm, alongside a salting value. The salt value is stored in the Jube.environment file, having being randomly created on application first use.
EnablePublicInvokeController True Most HTTP Endpoint require authentication, although an exception is the api/Invoke endpoint which is intended for integration. A boolean value needs to be set to allow the invocation API to be recalled on an open basis, and will in the event of False, return Not Found HTTP Status.
EnableEngine True A boolean value to indicate if the engine should be instantiated. The engine contains functionality such as Sanctions, Model Invocation, Archiving, Search Key Caching, Notifications, Case Automation, Counters, Tagging, Reprocessing and Exhaustive Training. In the absence of the engine, only functions required by the user interface are instantiated.
EnableCallback True A boolean value to indicate that HTTP callbacks are available - see HTTP Asynchronous Model Invocation. Paired with CallbackTimeout.
EnableMigration True A boolean value to indicate whether this instance should perform database Migration on startup - see Architecture. Disable and run the Fluent Migrator Command Line Runner separately where DDL permissions are unavailable at runtime.
EnableExhaustiveTraining True A boolean value to indicate that a thread should be started for the purpose of Exhaustive training. Given the computation expense of Exhaustive training only a single thread is available per instance.
EnableAdvancedAnalyticsTraining True A boolean value to indicate that a background worker should be started for the purpose of Advanced Analytics training. Submitted training runs are queued and picked up by this worker so that training does not saturate the user interface. Runs are claimed atomically, so several engine nodes may run the worker safely.
AdvancedAnalyticsTrainingPollSeconds 5 Seconds between polls of the Advanced Analytics training queue.
AdvancedAnalyticsTrainingMaxConcurrentRuns 2 The maximum number of Advanced Analytics training runs one engine executes at the same time.
AdvancedAnalyticsTrainingMaxConcurrentRunsPerTenant 1 The maximum number of Advanced Analytics training runs one tenant may have running at the same time, across all engines. Waiting tenants are served in turn.
AdvancedAnalyticsTrainingMaxRunSeconds 1800 The longest an Advanced Analytics training run may take. A run over the limit is cancelled and marked Failed.
AdvancedAnalyticsTrainingHeartbeatSeconds 10 Seconds between heartbeats a running Advanced Analytics training run writes to prove that its engine is alive.
AdvancedAnalyticsTrainingStaleSeconds 120 A running Advanced Analytics training run whose heartbeat is older than this many seconds is marked Failed because its engine stopped. Keep it well above the heartbeat interval.
AdvancedAnalyticsTrainingInlineMaxRows 20000 The most rows a submitted Advanced Analytics training run may carry inline in its arguments. Larger data belongs in the Advanced Analytics cache.
AdvancedAnalyticsTrainingInlineMaxBytes 16777216 The most bytes of inline rows, as json, a submitted Advanced Analytics training run may carry.
AdvancedAnalyticsTrainingOnlineMaxRows 2000 The most rows the immediate, synchronous Advanced Analytics training path accepts. Larger data must be submitted for the background worker.
ExhaustiveTrialsLimit 1000 For an Exhaustive training instance, the maximum number of trials to be performed (which is a random selection of input variables. This variable does not govern the evolution of a Neural Networks topology for each trial.
ExhaustiveMinVariableCount 5 For an Exhaustive trial, the minimum number of variables to be trained and topology evolve.
ExhaustiveMaxVariableCount 30 For an Exhaustive trial, the maximum number of variables to be trained and topology evolve.
ExhaustiveTrainingDataSamplePercentage 0.6 To avoid over-fitting, data available is randomly split between training, cross validation and testing. The percentage of data to be allocated to training.
ExhaustiveCrossValidationDataSamplePercentage 0.2 The percentage of data to be allocated to Cross Validation.
ExhaustiveTestingDataSamplePercentage 0.2 The percentage of data to be allocated to Testing.
ExhaustiveValidationTestingActivationThreshold 0.5 The threshold at which a recalled example will be allocated to the positive class.
ExhaustiveTopologySinceImprovementLimit 10 During a trial the internal topology will have processing elements and hidden layers evolved. The evolution process will terminate at the point a number of additional evolutions have taken place without improvement. The value at which evolution should terminate upon no improvement. Lesser performance will always terminate and carry forward as best topology.
ExhaustiveLayerDepthLimit 4 The maximum number of hidden layers in a topology exploration.
ExhaustiveLayerWidthLimitInputLayerFactor 4 A value when multiplied by the number of inputs for the trial represents the maximum number of processing elements for a hidden layer.
ExhaustiveTopologyComplexityLimit 10000 For topology evolution the maximum topology complexity (the total count of weights and biases, plus the number of weighted layers) allowed before termination and carrying forward of that topology as best.
ExhaustiveActivationFunctionExplorationEpochs 3 The number of epochs to be trained to evaluate then select an Activation Function.
ExhaustiveTopologyExplorationEpochs 3 The number of epochs to be trained to evaluate then evolve further a topology.
ExhaustiveTopologyFinalisationEpochs 20 With topology selected, the number of epochs to create a final model for testing.
ExhaustiveSimulationsCount 100 The number of random simulations to be processed through the final model for the purpose of testing and creation of description statistics for the positive class. Often known as Monte Carlo Model Simulation.
ExhaustiveAnomalyAutoBudgetSeconds 120 The time budget in seconds for the automatic anomaly detector evaluation of an Exhaustive Adaptation whose Anomaly Algorithm is Automatic. Candidates are evaluated in a fixed order and those not reached before the budget expires are recorded as skipped in the anomaly report. It bounds only the evaluation, not the fit of the chosen detector. Unparseable or non-positive values fall back to the default.
ExhaustiveChallengerModelBaseProbability 0.1 For a classification Exhaustive Adaptation, the base chance that a trial trains a challenger model (one of the AdvancedAnalytics classifier families, with dithered hyperparameters) instead of evolving the neural network topology. Ramps up towards 1 as the best topology found so far approaches ExhaustiveTopologyComplexityLimit, so challengers are tried more often once the network has grown complex. Only offered for trials whose selected variables are all non-categorical.
ExhaustiveRecallTimeout 1000 The time budget in milliseconds for the Exhaustive adaptation stage of a single invocation. It is checked between Exhaustive models and between input variables. When exceeded, the remaining models are skipped and have no score in the payload. A value of 0 or less disables the budget.
AdvancedAnalyticsStageTimeout 1000 The time budget in milliseconds for the Advanced Analytics adaptation stage of a single invocation. It is checked between models and between input variables. When exceeded, the remaining models are skipped and have no score in the payload. A value of 0 or less disables the budget.
AdvancedAnalyticsCacheTtlSeconds 3600 The lifetime in seconds of an Advanced Analytics cache entry (packed numeric arrays held for the LLM tools). Expired entries are treated as not found and are removed by the cleanup. A value of 0 or less, or an unparseable value, falls back to the default.
AdvancedAnalyticsCacheMaxEntryBytes 67108864 The largest single Advanced Analytics cache entry in bytes, 64 MiB by default. A larger entry is rejected. A value of 0 or less falls back to the default.
AdvancedAnalyticsCacheMaxTenantBytes 1073741824 The total of unexpired Advanced Analytics cache entries in bytes allowed per tenant, 1 GiB by default. An entry that would take the tenant over the limit is rejected. A value of 0 or less falls back to the default.
CallbackTimeout 1000 The time in milliseconds before callbacks ready for collection via HTTP are to be removed. Callbacks are only created if HTTP model invocation has been called using the async switch.
StreamingActivationWatcher True The default behaviour for the Activation Watcher streaming, in the absence of AMQP having been enabled, is via database streaming notifications. When false indicates that the database ActivationWatcher table, given also ActivationWatcherAllowPersist, should be polled for Watcher messages rather than subscribing to database streaming notifications. Given streaming notifications the message is dispatched to the notification service in the database synchronously. A typical connection string is of the form: HOST_NAME:PORT_NUMBER,password=PASSWORD where HOST_NAME is the host name of your server (e.g. localhost), PORT_NUMBER is the port number Redis is listening on (e.g. 6379) and PASSWORD is your redis server’s password (e.g. secret_password).
WatcherStreamHeartbeatIntervalSeconds 15 How often, in seconds, the Watcher Server-Sent Events stream writes a : keep-alive comment to an open connection while there is nothing to relay, so intermediate proxies do not time out an idle response.
WaitPollFromActivationWatcherTable 5000 In the event that of StreamingActivationWatcher and ActivationWatcherAllowPersist the polling interval to relay messaged from the ActivationWatcher database table to the watcher user interface page.
WaitTtlCounterDecrement 1000 In the event of TTLCounter being enabled to signify that the server should be responsible to decrementing TTL counter entries, the wait interval between decrement jobs in milliseconds
RedisConnectionString localhost The server or IP address of he Redis cluster in the connection string format supported by the c# Redis client NRedisStack and StackExchange.Redis.
RedisSentinelConnectTimeout 2000 Only used when RedisConnectionString sets serviceName= (Sentinel mode). The connect/response timeout in milliseconds for the Sentinel-discovery hop specifically - deliberately independent of RedisConnectionString’s own connectTimeout/syncTimeout, which govern the data connection to the resolved primary and are typically set more generously to tolerate ordinary command latency. Keeping this short means a slow or partially unreachable Sentinel quorum fails fast and hands back to the reconnect backoff quickly, rather than a stuck master re-resolution riding the same long timeout the data connection needs.
RedisSentinelSyncTimeout 2000 Companion to RedisSentinelConnectTimeout above - the Sentinel-discovery hop’s response timeout in milliseconds.
RedisSentinelConnectRetry 1 Only used when RedisConnectionString sets serviceName= (Sentinel mode). The number of connection attempts made against the Sentinel quorum before giving up for that cycle and handing back to ReconnectRetryPolicy’s backoff, rather than retrying the Sentinel hop itself repeatedly.
PgPoolClearDebounceMilliseconds 5000 The minimum interval in milliseconds between successive Npgsql pool clears for a given connection string. A pool clear is the correct response to a Patroni failover, since pooled connections may still point at a node that has been demoted, but the pool is process-global and keyed on the connection string, so every concurrently failing operation would otherwise clear the same pool on every one of its retries. Clearing once has the full intended effect; clearing repeatedly destroys each connection warmed since the previous clear and prevents the pool re-establishing during exactly the window it is needed. This debounce collapses that stampede to a single clear per failover episode. The default is comfortably shorter than HAProxy’s own detection of a demoted primary (inter 3s fall 3, so roughly nine seconds) while being long enough to absorb a burst of simultaneous failures. Raise it if failover storms still show repeated clears in the logs; lower it if a second genuine failover shortly after the first is being missed.
PgMaxConnectionRetries 10 How many times a failed Postgres connection or command is retried by the resilient Npgsql layer before the failure is allowed to surface. The wait between attempts is exponential and capped at thirty seconds, so the default spends roughly three and a half minutes (2+4+8+16 then 30 a time) before giving up. That is deliberate patience for a Patroni leader election, where the cluster genuinely may be unwritable for tens of seconds and an engine task has nowhere better to be. It is far less appropriate on a synchronous request path, where whoever asked has usually abandoned the request long before the retries are spent, and each waiting attempt holds its thread and its pool slot. Lower it for a deployment whose Postgres is local and whose callers are interactive; leave it alone where failover tolerance matters more than latency. Connection-pool exhaustion is deliberately excluded from retrying altogether, since the pool has already waited its own Timeout and retrying only re-queues behind the same starvation. Zero disables retrying entirely.
RedisReconnectRetryBaseDelay 100 The initial delay in milliseconds of the ExponentialRetry policy governing how fast StackExchange.Redis retries re-establishing the physical connection after it drops. Governs recovery speed once the connection is down, not how long an in-flight command waits - see RedisBacklogFailFast below for that.
RedisReconnectRetryMaxDelay 3000 The delay cap in milliseconds of the same ExponentialRetry reconnect policy, paired with RedisReconnectRetryBaseDelay above.
RedisBacklogFailFast False When False (StackExchange.Redis’s own default), a Redis command issued while the connection is unhealthy is queued and left to ride out its own SyncTimeout before failing - the mechanism behind a transaction being held for a long period during a genuine network outage, since every Redis call in that window queues and waits rather than failing immediately. When True, such commands fail as soon as the connection is known unhealthy instead, letting the caller react (skip/degrade/retry at its own level) rather than block. Only change this if the calling code is prepared to handle the resulting exception being thrown immediately rather than after a delay.
RedisCommandFlag 0 A command that controls the Redis client, pipeline and quorum. PreferMaster=0; 2=FireAndForget;4-DemandMaster; 8=PreferSlave; DemandSlave=12; NoRedirect=64; NoScriptCache=512.
CachePruneServer True A flag indicating if this instance is responsible for processing the Time To Live (TTL) deletion in both the Redis or Postgres Database cache.
WaitCachePrune 10000 The time in milliseconds between the cache being pruned for expired payload and latest entries.
Landlord True A boolean flag to support multi tenancy administration in the user interface. Landord is configured for the default administrator user only on installation. Landlord permissions are allocated in the Tenant Registry, where a tenant is taken to be super user.
Log4NetConfigFileLocationName null Will fall back to programmatic instantiation if unavailable. The XML configuration file allowing for advanced configuration and instantiation of the log4net logging library. This is helpful in the case of configuration of remote logging such as syslog. In the case of the RollingFileAppender all necessary settings are otherwise available via dedicated Environment Variables as follows.
Log4NetLogPath null Will fall back to the binary directory as target for logs, which is far from ideal and may not work in containers. The file path to write and rotate log files to, given the absence of the Log4NetConfigFileLocationName environment variable
Log4NetLogMaximumFileSize 500MB The maximum file size before the logs get rotated to new files, given the absence of the Log4NetConfigFileLocationName environment variable
Log4NetLogMaxSizeRollBackups 100 The maximum number of files to write before they are purged, given the absence of the Log4NetConfigFileLocationName environment variable
Log4NetLogLevel WARN The logging level to write log events out, given the absence of the Log4NetConfigFileLocationName environment variable
PreservationSalt null For the import and export of definition an environment specific salting value in keeping with the downloaded file bytes encryption scheme
LocalCache True To enable the local Least Recently Used (LRU) Cache for payload data in the Jube instance
LocalCacheFill True To enable a fill of the local LRU cache from Redis on the startup of the Jube instance based on the most recently used LruJournal keys in Redis, returning at the point the LRU Cache is at capacity as specified in the LocalCacheBytes Environment Variable. Requires LocalCache environment variable to be True.
LocalCacheBytes 1073741824 The maximum size of the local LRU cache before evictions.
RedisMessagePackCompression True To enable LZ4BlockArray compression for message pack serialization of Payload data.
RedisStorePayloadCountsAndBytes False To enable the informational storage of Payload data counts and bytes for visibility into model usage of Redis. Defaulted off this branch to reduce noise - only useful for multi-tenancy, and even then not always.
RedisPublishSubscribeEvents False To enable to publish\subscribe events of updates to the local LRU cache for clustered instances of Jube.
SearchKeyCacheServerIntervalType h The interval type for the launching of the search key cache calculation job. Accepted values: n (minutes), h (hours), d (days), m (months) - any other value is treated as d. Note n, not m, is minutes; m is reserved for months.
SearchKeyCacheServerIntervalValue 1 The interval value for the launching of the search key cache calculations job.
AMQPHeartbeatInterval s The unit for AMQPHeartbeatIntervalValue: s (seconds), n (minutes), h (hours) - any other value, including d, is treated as days.
AMQPHeartbeatIntervalValue 30 How often, in the unit set by AMQPHeartbeatInterval, the AMQP client sends heartbeats to the server to avoid the connection being closed.
CaseCreationThreads 4 The number of background thread responsible for the expensive case creation and upward classification processing.
MaxInvokeControllerRequestBytes 4000 The maximum number of bytes that can be JSON parsed for the invocation pipeline, beyond which an exception will be thrown.
ExhaustiveBatchRecallMaxRows 2000 The most rows a single batch recall request may carry against an Exhaustive or Advanced Analytics model. A request exceeding this is refused rather than truncated, so a caller is never silently given predictions for only part of what it sent. An unparseable or non-positive value falls back to 2000.
PartialResponseMessageSerialisation True When true, only return elements in the response payload on the basis of ResponsePayload switch being true in varius model entities, otherwise return the same comprehensive payload as stored in the Archive table. Disabling PartialResponseMessageSerialisation is useful when developing configurations where testing needs total visibility of invocation context payload.
RedisBackplane True In a load balanced environment the preference is not to use server affinity for the user interface, but this cases breakage in the Activation Watcher page. In the absence of server affinity Redis can be used as a backplane such that the connection does not need to be stateful.
DataProtectionRedisBackplane True In a load balanced environment ASP.NET Core Data Protection keys (e.g. cookie encryption) must be shared across nodes. When enabled, the Data Protection key ring is persisted to Redis using the RedisConnectionString rather than the local file system, so nodes share a consistent application name and key ring. Enabled by default: set to False on a single node that has no Redis.
SecretsPath null The file system path to look up secrets, where the file name is the key and the trimmed contents of the file is the value. This has special compatibility with Docker Secrets. If not provided, defaults to the application’s working directory. Secrets are only tokenised where the [@Key@] pattern exists inside the Environment Variable string.
JempFileLegacyEncryptionFallback True Preservation (.jemp) export files are AES encrypted with a random Initialisation Vector (IV) per file. Files exported before this hardening used a fixed IV, which is a weaker scheme. When True, import falls back to the legacy fixed-IV scheme if random-IV decryption fails, so older exports remain importable. Set to False once no legacy exports remain in use to remove the fallback attempt entirely.
ApiHmacKey null The HMAC-SHA256 secret used to sign and verify UserRegistryApiKey API keys (Base62-encoded, versioned payload). Must be set to a strong, unique, per-environment value before issuing API keys. Changing this value invalidates every API key issued under the old value immediately and with no overlap window - there is no dual-key transition support, so rotating it is an all-keys-dead-at-once event, not a graceful rollover. Plan API key reissuance for every integration before rotating this in a live environment.
RedisHsetOffloadToPostgres False When True, Redis HSET-shaped cache writes (see Cache Concepts) are instead served from a Postgres CacheSetHash table, using a covering index over Key, Field and each of the typed value columns to avoid heap access. This may outperform Redis for this access pattern on server-grade Postgres hardware, at the cost of moving load onto the primary database rather than Redis.
SecureHttpCookie False A boolean value to mark the authentication cookie as Secure, meaning the browser will only transmit it over HTTPS. Should be True for any environment served over HTTPS, which should be all environments other than local development.
StrictCookie True A boolean value that applies when SecureHttpCookie is True. True issues the authentication-jwt cookie as SameSite=Strict, so the browser never sends it on cross-site requests. False issues it as SameSite=Lax, which the browser still sends on top-level GET navigations, such as the redirect back from an identity provider. It has no effect when SecureHttpCookie is False, which already issues Lax. Default True.
SessionAbsoluteLifetimeInterval n The unit for SessionAbsoluteLifetimeIntervalValue: s (seconds), n (minutes), h (hours) - any other value, including d, is treated as days.
SessionAbsoluteLifetimeIntervalValue 720 The absolute maximum age of a login session, in the unit set by SessionAbsoluteLifetimeInterval, counted from the original login. A session is refreshed while it is used, but is never extended beyond this age: after it the user must log in again. The token carries the original login instant, so a refresh can never lengthen the session. Set to 0 to disable the absolute limit (not recommended).
MaxConcurrentConnections 10000 The maximum number of concurrent connections the web server accepts, protecting against connection exhaustion when no reverse proxy fronts the application. Set to 0 for no limit.
RevokedSessionCheckInterval s The unit for RevokedSessionCheckIntervalValue: s (seconds), n (minutes), h (hours) - any other value, including d, is treated as days.
RevokedSessionCheckIntervalValue 30 How often, in the unit set by RevokedSessionCheckInterval, open streaming connections (Watcher, Service Change) are re-checked against the database. A connection whose user has logged out, changed password, been locked, deactivated or deleted, or whose session has passed its absolute lifetime, is closed at the next check. Set to 0 to disable the check.
CsrfOriginCheck True When True, a state-changing request (POST, PUT, PATCH, DELETE) that is authenticated only by the authentication cookie must be provably same-origin: the browser’s Sec-Fetch-Site must be same-origin, or its Origin (or Referer) must match the site host (or X-Forwarded-Host behind a proxy). Requests that carry an Authorization header or an X-API-KEY are unaffected. Set to False only if a proxy makes the host impossible to match.
WafEnabled True Master switch for the content Web Application Firewall (see Web Application Firewall). When False the middleware passes every request straight through and the polling and flush background services do nothing. Enabled by default; review the seeded WafSignature rows and add any WafException rows the deployment needs, because a signature whose Drop flag is set will reject matching requests with 403.
WafRefreshInterval s The unit for WafRefreshIntervalValue: s (seconds), n (minutes), h (hours) - any other value, including d, is treated as days.
WafRefreshIntervalValue 30 How often, in the unit set by WafRefreshInterval, the WafSignature and WafException tables are re-read from the database, recompiled and swapped in atomically. Adding, editing or disabling a signature or exception takes effect at the next poll with no restart.
WafFlushInterval s The unit for WafFlushIntervalValue: s (seconds), n (minutes), h (hours) - any other value, including d, is treated as days.
WafFlushIntervalValue 10 How often, in the unit set by WafFlushInterval, captured matches queued in memory are bulk-inserted into the WafAttack table.
WafMaxInspectBytes 262144 The maximum number of request-body bytes the WAF middleware buffers and inspects. A JSON body larger than this, or a request declaring a larger Content-Length, is passed through without body inspection (its path and query string are still inspected). Prevents the WAF becoming a memory-exhaustion surface on large or streamed bodies.
WafMaxRegexTimeoutMilliseconds 100 The hard ceiling, in milliseconds, on each signature’s and exception’s regular-expression match timeout. A WafSignature.MatchTimeoutMilliseconds above this value is capped to it. A match that times out is treated as a non-match and never hangs a request, bounding the ReDoS risk of a table-driven pattern.
WafMaxInspectionMilliseconds 200 The overall time budget, in milliseconds, for inspecting one request across every field and every signature. WafMaxRegexTimeoutMilliseconds only bounds a single regex match; without this, a request with several fields, or one that runs several signatures close to their own timeout, can add those costs up into a multi-second delay even though no single match ever hangs. Once the budget is exceeded, inspection of that request stops and every signature not yet evaluated is treated as a non-match (fail open), the same as an individual match timing out.
ElementSymmetricEncryptionKey SuperSecretEncryptionKeyGoesHere The symmetric key used by the AES string encryption helper available to Inline Scripts, for encrypting individual payload elements at the point of use (for example, before writing a value to a Search Key). NOTE: this ships with a well-known placeholder default - it must be changed to a securely generated, per-environment value before any Inline Script relies on it, otherwise encrypted values are trivially reversible by anyone with a copy of this documentation.
EnableMultifactorAuthentication False A boolean value to enable RSA SecurID-led Multi-Factor Authentication (MFA) as a second authentication step, enforced regardless of whether the first step was Negotiate or Username and Password authentication. Not applicable to OAuth sign-in, which is assumed to always offload MFA to the identity provider itself.
MultifactorAuthenticationEndpoint http://localhost:5001/api/mfa The RSA Authentication Manager (SecurID) endpoint used to validate the one-time passcode. Points at the bundled mock endpoint by default (accepts only the OTP value 123456), for environments without access to a real RSA sandbox.
MultifactorAuthenticationApplicationId MyApplicationGuidOrSomeSuch The application identifier RSA Authentication Manager was configured with for this integration. Replace with the real application identifier issued for your RSA Authentication Manager tenant.
MultifactorAuthenticationClientKey SomethingSecretForTheClientKeyHeader A shared secret sent as a client-key header on every request to MultifactorAuthenticationEndpoint. Replace with a securely generated value shared with the RSA Authentication Manager deployment; the placeholder default must not reach production.
PasswordAttempts 3 The number of consecutive incorrect password attempts allowed before the user account is locked out.
EnablePasswordLockoutReset True A boolean value, the deliberate and explicit switch for whether a per-user database lockout (PasswordLocked) is ever allowed to auto-expire. When True (the default), PasswordLockoutInterval/PasswordLockoutIntervalValue below govern how long a lock lasts before the next login attempt clears it automatically. Set to False for a permanent, admin-only-unlock lockout regardless of those two settings - useful where an operator would rather investigate every lockout than have any of them clear themselves. Auto-expiry exists because an unauthenticated caller who only knows a victim’s username could otherwise lock that account forever with PasswordAttempts wrong guesses; see LoginIpRateLimitAttempts below for the complementary per-source-IP mitigation of the same attack from a single source.
PasswordLockoutInterval n The unit for PasswordLockoutIntervalValue: s (seconds), n (minutes), h (hours) - any other value, including d, is treated as days.
PasswordLockoutIntervalValue 30 How long, in the unit set by PasswordLockoutInterval, a per-user database lockout (PasswordLocked) lasts before it auto-expires on the next login attempt, when EnablePasswordLockoutReset is True. A value of 0 or negative also disables auto-expiry as a defensive fallback, but EnablePasswordLockoutReset=False above is the explicit, intended way to choose a permanent lockout.
LoginIpRateLimitAttempts 1000 The number of failed login attempts, across any username, permitted from a single source IP within the window set by LoginIpRateLimitInterval/LoginIpRateLimitIntervalValue before further attempts from that IP are rejected outright. Mitigates a distributed account-enumeration/lockout attempt from one source that PasswordLockoutIntervalValue’s per-user counter alone cannot see.
LoginIpRateLimitInterval s The unit for LoginIpRateLimitIntervalValue: s (seconds), n (minutes), h (hours) - any other value, including d, is treated as days.
LoginIpRateLimitIntervalValue 60 The sliding time window that LoginIpRateLimitAttempts is measured over, in the unit set by LoginIpRateLimitInterval.
EnableGlobalHttpRateLimit False A boolean value, the deliberate and explicit switch for a per-source-IP rate limit applied to every HTTP request except the high-throughput /api/Invoke transaction-submission path, which is always exempt. Off by default since it is a broad behavioural change affecting all traffic, not just authentication; enable it to add a DDoS/flood backstop in front of the rest of the API and UI. A request over the limit gets HTTP 429 (Too Many Requests) and never reaches the endpoint.
GlobalHttpRateLimitAttempts 2000 The number of requests, from a single source IP, permitted within the window set by GlobalHttpRateLimitInterval/GlobalHttpRateLimitIntervalValue before further requests from that IP get HTTP 429. Every request counts, not just failed ones - unlike the per-user password lockout, there is no concept of a “failed” request at this layer.
GlobalHttpRateLimitInterval s The unit for GlobalHttpRateLimitIntervalValue: s (seconds), n (minutes), h (hours) - any other value, including d, is treated as days.
GlobalHttpRateLimitIntervalValue 60 The sliding time window that GlobalHttpRateLimitAttempts is measured over, in the unit set by GlobalHttpRateLimitInterval.
RateLimitBackplane True A boolean value controlling how EnableGlobalHttpRateLimit’s counter is kept. False counts in memory, local to each node - fine for a single instance, but a client can get GlobalHttpRateLimitAttempts on every node in a cluster before HAProxy has spread enough load for any one of them to notice. True (the default) counts through Jube.Cache’s existing Redis connection instead (the same HINCRBY-plus-expiry pattern used by TTL Counters), so every node behind HAProxy shares one counter per source IP and the limit holds cluster-wide regardless of which node an attacker lands on. Requires Redis (RedisConnectionString) to be reachable; a Redis failure fails open (the request is allowed through) rather than blocking traffic.
CacheTtlDeleteLimit 1000 The per-cycle deletion batch limit (a SQL/sorted-set range LIMIT, not a Redis HSCAN) for the general cache-prune background task that removes reference-date-expired entries from the Payload and Payload-Latest caches, so a large expired backlog drains over several bounded passes rather than being loaded into memory in one operation. Despite the name, this is not specific to TTL Counters - see TtlCounterEntryDeleteLimit below for that.
TtlCounterEntryDeleteLimit 200 The maximum number of expired TTL Counter entries processed per administration cycle, so a large backlog drains over several cycles rather than flooding a single concurrent-task burst. This is the TTL-Counter-specific equivalent of CacheTtlDeleteLimit above, and is a separate setting.
TtlCounterAdministrationMaxConcurrency 25 The maximum number of expired TTL Counter entries decremented/deleted concurrently within a single administration cycle (a semaphore, not a batch size - see TtlCounterEntryDeleteLimit above for that). Raising it increases Redis/Postgres concurrency during a large backlog drain; lowering it reduces peak load at the cost of a slower drain.
PasswordAsymmetricEncryption False A boolean value to enable RSA asymmetric encryption of the password field in transit, on top of the existing SHA-256 wire hash (see WirePasswordHash below). When True, the browser encrypts the password with PasswordAsymmetricEncryptionPublicKey before sending it, and the server decrypts it with PasswordAsymmetricEncryptionPrivateKey. Jube refuses to start with this set to True unless both key Environment Variables below are also explicitly set - see PasswordAsymmetricEncryptionPrivateKey.
PasswordAsymmetricEncryptionPrivateKey null The Base64-encoded PEM private key used to decrypt the password field when PasswordAsymmetricEncryption is True. Generate a fresh, per-environment RSA keypair for this - Jube validates at startup that this and PasswordAsymmetricEncryptionPublicKey are both set whenever PasswordAsymmetricEncryption is True, and refuses to start otherwise, so a shared or default keypair can no longer be used by omission.
PasswordAsymmetricEncryptionPublicKey null The Base64-encoded PEM public key the browser encrypts the password with. See PasswordAsymmetricEncryptionPrivateKey - both must be set together, and Jube will not start with PasswordAsymmetricEncryption True and either one missing.
SessionCookie True A boolean value to control whether the authentication cookie is a session cookie. When True, the cookie is not persisted and the user is signed out when the browser is closed; when False, the cookie persists per its normal expiry regardless of the browser closing.
AssumeLocalDateInPayloadExtraction True A boolean value controlling how dates without explicit timezone information, extracted during payload extraction (Request XPath, Inline Functions, Inline Scripts), are interpreted. When True they are assumed to be in the server’s local time and converted to UTC accordingly; when False they are assumed to already be UTC. Set to match the timezone convention of the systems sending transactions to Jube.
ActivationRuleIdempotency False A boolean value to enable idempotency guarantees on Activation Rules - TTL Counter increments, Case Creation and Notification dispatch are each applied at most once per transaction lifecycle, even under reprocessing or retry. Idempotency records are only written to Redis when this is True, so leave it False in memory-constrained deployments that do not use Reprocessing, to avoid the extra Redis writes. Idempotency Set keys in Redis have no TTL of their own and are only removed when the underlying transaction’s payload is deleted, so enabling this on a deployment that never deletes/expires payloads will accumulate idempotency keys for as long as the payload itself is retained.
LruJournalMaxAgeInterval h The interval unit for how long an entry may remain in the LruJournal Redis sorted set - used only to prime the local LRU cache on startup (LocalCacheFill) - before a background task prunes it. Paired with LruJournalMaxAgeValue below. Accepted values: n (minutes), h (hours) - any other value, including d, is treated as days. Similar to, but not identical to, SearchKeyCacheServerIntervalType above - this one has no dedicated m (months) case.
LruJournalMaxAgeValue 1 The interval value paired with LruJournalMaxAgeInterval, e.g. 1 and h together mean entries older than one hour are pruned.
WaitLruJournalPrune 10000 The interval in milliseconds between background LruJournal pruning cycles, which evict entries older than LruJournalMaxAgeInterval/LruJournalMaxAgeValue. The same age check also runs a second time during cache warming itself, so a slow prune cycle cannot cause a stale entry to be used to prime the cache.
WaitHashCacheAssemblyObservability 60000 The interval in milliseconds between snapshots of the compiled rule assembly cache (HashCacheAssembly) to the HashCacheAssemblyInstance/HashCacheAssemblyInstanceJournal/HashCacheAssemblyInstanceEntry tables - see Rule Compilation Algorithm.
SMTPUseDefaultCredentials False A boolean value to indicate that the SMTP client should use the process’s default network credentials rather than SMTPUser/SMTPPassword. Useful where the SMTP relay trusts the sending host/network rather than requiring explicit credentials.
SMTPEnableSsl True A boolean value to indicate that the SMTP client should negotiate SSL/TLS with the SMTP server.
OutboundHttpRsaAmCertificateBypass False A boolean value to bypass all TLS certificate validation for outbound HTTP requests to the RSA Authentication Manager MFA endpoint. Intended for sandbox/UAT RSA deployments behind a self-signed or otherwise unverifiable certificate chain. Do not enable in production - prefer OutboundHttpRsaAmCertificateThumbprint below, which trusts one specific certificate rather than disabling validation entirely. Currently implemented for the RSA token endpoint only.
OutboundHttpRsaAmCertificateThumbprint null A SHA1 certificate thumbprint to explicitly trust for outbound HTTP requests to the RSA Authentication Manager MFA endpoint, for chains that do not terminate at a verifiable root but where disabling validation entirely (OutboundHttpRsaAmCertificateBypass) is not acceptable. Scoped to the RSA MFA client only (Jube.Mfa/Mfa.cs) - not applied to HTTP Adaptation or any other outbound webhook, each of which has its own independent, unrelated TLS handling.
OAuthAuthentication False A boolean value to enable OAuth 2.0 / OpenID Connect (authorization code flow with PKCE) as a third authentication scheme, alongside Username/Password and Negotiate. See Authentication Concepts. Mutually exclusive with NegotiateAuthentication - both cannot be True at once - and Username/Password authentication (including password changes) is disabled whenever either is True.
OAuthAuthority null The OpenID Connect authority (issuer) URL of the identity provider, used for OIDC discovery.
OAuthClientId null The OAuth client identifier registered with the identity provider for this Jube instance.
OAuthClientSecret null The OAuth client secret registered with the identity provider for this Jube instance.
OAuthForceRedirect null On conclusion of the full OAuth authentication the redirect url that will override anything passed for redirection.
OAuthForceGet False A boolean value to force the OAuth/OIDC callback to use GET/query-string responses instead of the default form_post response mode, for identity providers that require it.
OAuthSignInInterstitial False A boolean value. When True, the OAuth/OIDC callback answers the post-login step with a 200 carrying a Refresh header to the target, rather than a 302, so the browser starts the final navigation from this origin. This lets the authentication-jwt cookie stay SameSite=Strict, which the browser would otherwise withhold on the redirect that follows the identity provider’s cross-site return. Targets that fail validation still receive the 302. Default False keeps the 302.
OAuthPrompt null The OpenID Connect prompt parameter sent on every redirect to the identity provider, e.g. login or select_account. Jube’s own logout only deletes its local authentication-jwt cookie and revokes the server-side token - it never signs the user out of the identity provider’s own SSO session - so without this set, a subsequent challenge can silently re-authenticate the user from that still-live SSO session. Setting this to login forces the identity provider to re-prompt for credentials on every redirect instead. Left null (the default), no prompt parameter is sent and the identity provider’s own default behaviour applies.
SanctionsLevenshteinMaxDistanceRatio 0.3 The server-wide default for the maximum Levenshtein edit-distance-to-token-length ratio permitted for a sanctions token match, before per-model MaxDistanceRatio override. See Sanction Searching.
SanctionsLevenshteinMaxCoverageRatio 2.0 The server-wide default for the maximum coverage ratio permitted for a sanctions token match, before per-model MaxCoverageRatio override. See Sanction Searching.
UseForwardedHeaders True A boolean value to enable ASP.NET Core’s forwarded-headers middleware, transposing X-Forwarded-For/X-Forwarded-Proto headers from an upstream reverse proxy or load balancer onto the request context. Important for OAuth redirect URIs and remote-IP logging to be correct when Jube sits behind a proxy.
EnableOpenTelemetry False A boolean value to enable OpenTelemetry tracing and metrics export for the service layer (Jube.Service). When enabled and OpenTelemetryBackendEndpoint is not set, an OTLP exporter is configured from the standard OTEL_EXPORTER_OTLP_* Environment Variables instead. The /api/invoke transaction ingress is excluded from request tracing. When disabled the instrumentation calls are still made but are no-ops.
OpenTelemetryBackendEndpoint null The base URL of an OTLP/HTTP backend to export traces/metrics/logs to when EnableOpenTelemetry is True, e.g. http://localhost:4318 – explicit and owned by Jube’s own configuration rather than the OTel SDK’s own OTEL_EXPORTER_OTLP_ENDPOINT env var auto-detection (still used as a fallback when this is left unset). Exporting is bounded, not buffered – a full export queue drops the newest item rather than growing without limit, and a dead/unreachable backend fails fast (short fixed timeouts) rather than backing up; see OTLP Dispatch Counter for how every dispatch attempt, failure and drop is captured as both a live counter and a browsable per-minute table. See also Log-Derived OpenTelemetry Counters and Jube.OpenTelemetryListener for a disposable local test target.
OpenTelemetryMetricCaptureSamplePercentage 100 The percentage, from 0 to 100 (values outside the range are clamped), of OpenTelemetry metric measurements from the Jube, .NET runtime, HTTP, ASP.NET Core and process meters that are captured into the OpenTelemetryMetric table used by the monitoring pages. 100 captures every measurement; lower values sample, which reduces database volume on busy instances at the cost of resolution. Read once at startup.
EnableServiceChangeStream False A boolean value to enable publishing of service-layer change events (create/update/delete) so open Blazor/browser sessions can live-refresh. Requires RedisBackplane for cross-node delivery; single-node deployments fan the events out in-process. When disabled, the service layer still calls the publisher but it is a no-op.
ServiceChangeStreamHeartbeatIntervalSeconds 15 How often, in seconds, the Service Change Server-Sent Events stream writes a : keep-alive comment to an open connection while there is nothing to relay, so intermediate proxies do not time out an idle response.
ArchiverWarnThresholdMilliseconds 10000 The duration in milliseconds a single background Archiver stage (BuildArchiveJson, CaseCreationDispatch, RdbmsArchiveWrite, BulkCopyArchiveBuffer) may take before a row is captured to Archiver Warning and jube.engine.archiver.warn.count is incremented. Set much higher than an in-thread invoke trace threshold, since these stages are I/O-bound background work (RDBMS writes, buffered bulk copies) that is routinely slow by nature – this should only trip for genuinely abnormal slowness, not everyday operation.
CaseCreationWarnThresholdMilliseconds 10000 The duration in milliseconds a single background case creation stage (ExistingCasePriorityLookup, WorkflowStatusLookupAndPersist, Notification, HttpEndpoint) may take before a row is captured to Case Creation Warning and jube.engine.casecreation.warn.count is incremented. Set much higher than an in-thread invoke trace threshold for the same reason as ArchiverWarnThresholdMilliseconds above – notification/webhook callbacks in particular are routinely slow by nature.
EtcdEndpoints null A comma separated list of etcd client endpoints in the form host:port that the infrastructure health metrics poll for cluster orchestration state (members, leader, events). When empty, endpoints are discovered by DNS using EtcdDiscoveryPrefix, EtcdPatroniDiscoveryMaxNodes and EtcdClientPort.
EtcdDiscoveryPrefix etcd The DNS host name prefix used when EtcdEndpoints is empty. The numbers 1 to EtcdPatroniDiscoveryMaxNodes are appended (etcd1, etcd2, and so on) and each name that resolves is used as an etcd endpoint on EtcdClientPort.
EtcdClientPort 2379 The etcd client port appended to each host discovered through EtcdDiscoveryPrefix. It is not applied to endpoints given explicitly in EtcdEndpoints. An unparseable value falls back to 2379.
EtcdClientUsername null The etcd username the infrastructure health metrics authenticate as when polling member/health status and Patroni’s DCS history key. etcd client auth is mandatory in the Jube Cluster deployment (see Deploying with Jube Cluster), so this and EtcdClientPassword must both be set there; left null, the samplers make unauthenticated calls, which only works against an etcd cluster with auth disabled.
EtcdClientPassword null The password for EtcdClientUsername. Resolved from a Docker secret via the [@Key@] convention in the Jube Cluster deployment, the same as every other password-shaped Environment Variable.
PatroniEndpoints null A comma separated list of Patroni REST API endpoints in the form host:port that the infrastructure health metrics poll for cluster role, lag and timeline. When empty, endpoints are discovered by DNS using PatroniDiscoveryPrefix, EtcdPatroniDiscoveryMaxNodes and PatroniApiPort.
PatroniDiscoveryPrefix patroni The DNS host name prefix used when PatroniEndpoints is empty. The numbers 1 to EtcdPatroniDiscoveryMaxNodes are appended (patroni1, patroni2, and so on) and each name that resolves is used as a Patroni endpoint on PatroniApiPort.
PatroniApiPort 8008 The Patroni REST API port appended to each host discovered through PatroniDiscoveryPrefix. It is not applied to endpoints given explicitly in PatroniEndpoints. An unparseable value falls back to 8008.
EtcdPatroniDiscoveryMaxNodes 5 The highest number tried when discovering etcd and Patroni hosts by DNS: with the default, etcd1 to etcd5 and patroni1 to patroni5 are resolved. It applies to both discoveries and is ignored for endpoints given explicitly. An unparseable value falls back to 5.
PatroniDcsNamespace /service/ The etcd key namespace under which Patroni keeps its cluster state (its dcs namespace setting). The Patroni scope name is appended to read the cluster history events that are recorded as Patroni cluster events, so it must match the namespace configured on the Patroni nodes.
EnableInfrastructureHealthMetricsPurge True Whether the infrastructure health metric purge cycle runs at all. Set False to disable purging outright, rather than disabling it indirectly through InfrastructureHealthMetricsPurgeDeleteLimit=0 or an impractically large WaitInfrastructureHealthMetricsPurge.
DockerMonitoringProxyUrl null The address of Jube.Monitoring’s own /proxy endpoint – the only address this service ever reaches for Docker data, never docker.sock directly, since this process is never given that socket. Null by default, naming a specific sidecar instance’s address; Docker container/host/event sampling is simply skipped (not an error) while unset, same as EtcdEndpoints/PatroniEndpoints above.
WaitInfrastructureHealthMetricsPurge 60000 The duration in milliseconds between purge cycles of the infrastructure health metric tables (application and container logs, Docker host, container and event metrics, .NET runtime metrics, etcd member status and events, OpenTelemetry metrics, OTLP dispatch counters and Patroni cluster events).
InfrastructureHealthMetricsPurgeIntervalType d The unit of the retention period for the infrastructure health metric tables: d days, h hours, n minutes, s seconds, m months or y years. Any other value is treated as days.
InfrastructureHealthMetricsPurgeIntervalValue 7 The number of InfrastructureHealthMetricsPurgeIntervalType units of infrastructure health metric data to keep. Rows older than that are deleted, so 7 with a type of d keeps seven days.
InfrastructureHealthMetricsPurgeDeleteLimit 1000 The maximum number of rows deleted in one chunk, per table, in a purge cycle. A purge loops in chunks of this size until nothing older than the retention period remains, which keeps individual delete transactions short.
PurgeConnectionString null An optional, separate connection string used only by the infrastructure health metric purge cycle, falling back to ConnectionString when unset. Useful to grant DELETE on just the fixed set of tables that purge chunk-deletes from (see Jube.Cluster/database.sh’s jube_purge grants) to a dedicated credential, while ConnectionString itself stays DELETE-less - see the restricted Postgres users note on Deploying with Docker.
EnableAskJooby False The explicit switch for whether AskJooby will actually attempt a live reply – checked by AskJoobyHub.cs (server-side, before every StartConversationAsync) and mirrored to the client as window.AskJoobyHasInferenceBackend (see _Layout.cshtml) so AskJooby.js/AskJoobyWidget.js know before ever opening the SignalR connection, not only after a round trip. Deliberately not inferred from whether AskJoobyApiKey happens to be blank – an explicit True/False reads as what it means and can’t be silently defeated by a key that’s merely unset versus one that’s actually wrong. False by default: the page and the floating widget bubble are never hidden either way (gated only by the AskJooby permission), but with this off Jooby opens with a static, honest “no backend configured yet” message – naming AskJoobyApiEndpoint/AskJoobyApiKey/AskJoobyModel and a few example providers – instead of attempting a call that would only fail. Set this to True only once those three are actually pointed at a real, working OpenAI-API-compatible endpoint; turning it on without them still tries the request and fails the ordinary way, it does not itself validate the other three.
AskJoobyApiEndpoint https://inference.do-ai.run/v1 The base URL of the OpenAI-compatible chat/embeddings API used by AskJooby. Defaults to DigitalOcean Gradient’s OpenAI-compatible endpoint. Set this to a different inference provider’s OpenAI-compatible base URL (a local Ollama instance, Azure OpenAI, etc.) alongside AskJoobyApiKey if not using DigitalOcean Gradient.
AskJoobyApiKey null The API key sent as the bearer credential to AskJoobyApiEndpoint. Required by OpenRouter’s endpoint (the default) and most hosted providers; not required for a local Ollama instance. Prefer passing it via the [@AskJoobyApiKey@] Docker Secrets token (see below) rather than a raw Environment Variable, since this value is a genuine secret.
AskJoobyModel deepseek-v4.1-flash The chat model name requested from AskJoobyApiEndpoint (the Worker – the agent that actually answers), also used as-is to generate a conversation’s auto-title (GenerateTitleAsync) and its “what’s next” follow-up chips (GenerateSuggestionsAsync); there is no separate title/suggestions model setting, because generating those from a different model than the one that wrote the reply risks drift or misreading relative to the reply being summarised. Defaults to DeepSeek V4.1 Flash on DigitalOcean Gradient: fast, reliable tool-calling, and well-grounded replies at a reasonable cost. Set to the target provider’s model identifier (e.g. qwen2.5:7b for a local Ollama instance) when AskJoobyApiEndpoint points elsewhere. This is a fast-moving space – re-evaluate this default periodically rather than assuming it stays the best choice.
AskJoobyEmbeddingModel bge-m3 The embedding model name requested from AskJoobyEmbeddingApiEndpoint for AskJooby’s RAG retrieval. Defaults to BGE-M3 on DigitalOcean Gradient (1024-dim dense output, matching the AskJoobyRagChunk.Embedding column); set to the target provider’s embedding model identifier when the endpoint points elsewhere. BGE-M3 is not instruction-aware and the query is embedded exactly as the caller wrote it, with no task prefix – there is deliberately no instruction-prefix option, since a prefix on a model that does not expect one only pollutes the vector. What that costs in query-side intent is recovered by the reranker instead; see AskJoobyRerankerModel. Changing this to a model of a different dimensionality requires the index to be rebuilt.
AskJoobyEmbeddingApiEndpoint https://inference.do-ai.run/v1 The OpenAI-compatible base URL the embedding model is requested from. It carries a default of its own rather than inheriting AskJoobyApiEndpoint, because the inheritance is only safe where the chat provider also serves embeddings and several major ones do not – DeepSeek’s API is chat completions only, so a DeepSeek chat deployment that inherited the endpoint answered the first embedding of the first turn with an HTTP 400 and the conversation never opened. This is a BASE address: the client appends /embeddings itself, so a value with the route already on the end is posted to …/embeddings/embeddings and rejected with a 400 that names neither the doubled path nor embeddings as the cause; startup warns at WARN when it sees one. The embedding client is only built separately when this or AskJoobyEmbeddingApiKey actually differs from the chat pair, so a single-provider deployment keeps one connection pool.
AskJoobyEmbeddingApiKey null The credential for AskJoobyEmbeddingApiEndpoint. Left blank (the default) the AskJoobyApiKey is reused, which is right when both models are served by the same provider and wrong as soon as they are not – and since the embedding endpoint now defaults to OpenRouter while the chat endpoint may point anywhere, that is the common case rather than the rare one. Startup warns at WARN when the two endpoints are on different hosts and this is blank, because a chat provider’s key presented to another provider is a 401 on the first embedding and reads nothing like a credential problem. A blank key against an endpoint that wants none is sent as the literal “not-needed”, the same as for the chat endpoint.
AskJoobyRerankerModel bge-reranker-v2-m3 The cross encoder asked to rerank retrieval candidates, posted to AskJoobyRerankerApiEndpoint. Defaults to BAAI’s bge-reranker-v2-m3. Reranking is a second stage: the first embeds the query once and asks the database for the nearest chunks, which is cheap and blunt enough to put the right passage fourth; the reranker reads the query and each candidate together and scores the pair, which is far more accurate and far too expensive to run over a corpus. It matters more since the move off an instruction-aware embedder, because it is the stage that sees the query as written against the passage as written. Both halves must be set – this and the endpoint – or retrieval runs in one stage exactly as before and says so once at INFO on startup.
AskJoobyRerankerApiEndpoint https://inference.do-ai.run/v1 The full URL of the rerank route. Given in full rather than as a host, because reranking is not an OpenAI route and does not share the path conventions the chat and embedding clients rely on, so nothing here guesses a suffix. Two response shapes are accepted without configuration: an object carrying a results array with relevance_score, which is what Cohere, vLLM and most hosted providers answer, and a bare array with score, which is what Text Embeddings Inference answers. Defaults to DigitalOcean Gradient. Nothing breaks if the route is wrong or goes away: a reranker that answers anything other than success is logged at WARN with the provider’s own response body and that search falls back to the retrieval order. Blank it to leave retrieval in one stage and stop the attempt.
AskJoobyRerankerApiKey null The credential for AskJoobyRerankerApiEndpoint, sent as a bearer token. Falls back to AskJoobyApiKey when blank, so a deployment serving the reranker from the same provider as the chat model configures nothing extra – and warns at WARN when it does not, on the same reasoning as AskJoobyEmbeddingApiKey.
AskJoobyRagCandidateCount 40 How many candidates the first stage retrieves before the reranker and the promotion pass cut them down to what was asked for. The corpus is built for this: search returns match targets, which are rows sized to be found rather than to be read, so a wide first stage and a precise second is the shape it expects. Widening is by count only – AskJoobyRagMinScore is still applied by the first stage, because quietly ignoring a tuned retrieval floor would change what is searched rather than only how it is ordered. Every candidate is sent to the reranker, so this is also what a search costs there; 40 is the low end of the band the protocol recommends and the place to start before measuring. Below the number of Bites requested it has no effect. A provider may cap how many documents one rerank call accepts, and exceeding it fails that call rather than truncating it, which costs the second stage on every search while the first stage carries on unaffected; the reason is logged at WARN with the provider’s own wording.
AskJoobyRagMaxCandidateCharacters 3500 The most characters of each first-stage candidate that are kept before it is handed to the reranker. A candidate longer than this is truncated rather than sent whole, since the reranker bills and scores per document and a handful of oversized candidates would otherwise dominate both the cost and the latency of a search that is meant to run AskJoobyRagCandidateCount of them in one call. 0 or an unparsed value falls back to 9000 in code rather than disabling the cap.
AskJoobyRagPromotionBudgetCharacters 6000 The largest a parent may be and still be returned in place of the children that matched it. Promotion needs two things and only two: a parent inside this budget, and two or more of its children surviving the rerank. Several hits in one place is the strongest evidence in the corpus that the parent is the right answer. The chunk tree is deliberately not depth-uniform, so promoting blind is unsafe: one hop from a MemberBlock is its member at a median of about three thousand characters, but one hop from a member is its whole type at a median of sixteen thousand and a maximum over three hundred thousand. The budget is what makes one rule safe across both – an oversized parent fails the test and its children are sent instead, so this also behaves correctly against an index built before the finer tier existed. 0 disables promotion and returns exactly what matched.
AskJoobyRerankerMinScore -100 The lowest rerank score a Bite may carry and still be returned. This is the cross encoder’s own scale and is not comparable with AskJoobyRagMinScore, which the first stage applies to a cosine distance: the two measure different things and each needs its own floor. The default is a sentinel low enough to never exclude anything rather than 0, because a cross encoder may answer with a raw, uncalibrated logit rather than a 0-1 probability – a genuinely relevant Bite can still score deeply negative, and a 0 floor was found in practice to discard almost every real match on almost every query with the reranker this was tuned against. Raise it only after sampling the configured reranker’s own scores for known-good and known-bad matches; a different reranker may use a different scale entirely. A reranker that cannot be reached is not a rejection – the search falls back to the retrieval order rather than coming back empty.
AskJoobyEnableReranking True Whether a RAG search’s first-stage candidates are sent through the reranker at all before promotion and the cut to the requested count. False skips that second stage entirely and returns the first stage’s own ranking, trading precision for the latency and cost of the reranker call.
RServerUrl http://localhost:8080 The base URL of the Jube.R server. The compose file points the jube service at http://jube-r:8080, the internal service name.
RServerHttpTimeout 330000 Milliseconds one HTTP call to the R server may take. Set above every timeout the call could otherwise race: ScriptExecutionService’s own per-call guard (up to 310 seconds: a 300-second script timeout plus a 10-second grace) and the R server’s own mirai daemon backstop (up to 305 seconds: a script’s own timeout plus a 5-second grace, see Jube.R/plumber.R) both need the chance to return a clean result first, so the server – not this client – decides when a run times out.
RServerMaxConnections 10 The most concurrent HTTP connections this instance opens to the R server.
RServerValidateSsl True Whether the R server’s TLS certificate is validated. Turn off only for a test endpoint with a self-signed certificate.
EnableAskJoobyR True The deployment-wide kill switch for the whole sandboxed-R-script capability (CheckRServerHealth, ListRPackages, ValidateRScript, RunScript), checked by CrossCutToolBinder.Bind before any of those four tools are offered to an agent at all. This sits alongside, not instead of, the per-caller RScriptExecution/SandboxedScriptExecution permission check – a deployment that wants the capability gone for everyone regardless of who holds that permission sets this False rather than trying to revoke the permission from every role. False removes the tools from the bound set entirely, the same way an AskJoobyReadOnly deployment never offers a Write/Delete tool in the first place, rather than leaving them offered and failing the call at runtime.
AskJoobyValidationApiEndpoint null The OpenAI-compatible endpoint the validator/reviser calls, when it must run on a different backend from the Worker. Blank (the default) inherits AskJoobyApiEndpoint. Falls back per setting and not per pair, so a validator on the same endpoint under a different key sets only AskJoobyValidationApiKey. Where endpoint and key both match the Worker’s and AskJoobyValidationModel is the same model, the validator reuses the Worker’s client outright rather than opening a second connection pool.
AskJoobyValidationApiKey null The credential for AskJoobyValidationApiEndpoint. Blank inherits AskJoobyApiKey.
AskJoobyEnableAiAnalysis True Whether a chat’s Technical Audit download also runs an LLM analysis pass over the run, health, RAG-gap, RAG-performance and context-efficiency sections, writing the result into the audit zip. On by default even though it is a second model call per audit export rather than per turn; the analysis model AskJoobyAiAnalysisModel can be set to a stronger, slower model than the Worker’s own, chosen for depth over turn latency.
AskJoobyAiAnalysisModel deepseek-v4.1-flash The model AskJoobyChatAuditService calls for the Technical Audit’s AI analysis pass, independent of AskJoobyModel so a heavier model can be used for an occasional audit export than for every turn.
AskJoobyAiAnalysisApiEndpoint null The OpenAI-compatible endpoint AskJoobyAiAnalysisModel is called on. Blank inherits AskJoobyApiEndpoint.
AskJoobyAiAnalysisApiKey null The credential for AskJoobyAiAnalysisApiEndpoint. Blank inherits AskJoobyApiKey.
AskJoobyAiAnalysisTimeoutSeconds 60 How long the Technical Audit’s AI analysis call may run before it is treated as failed and the audit zip instead records the failure reason and the three settings to check. 0 or an unparsed value falls back to 60.
AskJoobyAiAnalysisEnableThinking False Whether the Technical Audit’s AI analysis call requests extended thinking from AskJoobyAiAnalysisModel.
AskJoobyAiAnalysisThinkingEffort 0 The thinking effort requested on the Technical Audit’s AI analysis call when AskJoobyAiAnalysisEnableThinking is True.
AskJoobyAiAnalysisCodeBudgetCharacters 16000 The character budget for each of the two code-grounding sections (orchestration code and the agents actually involved) assembled from the RAG corpus and given to the Technical Audit’s AI analysis call as context. 0 or an unparsed value falls back to 16000.
AskJoobyThinkingDisableExplicitly True Whether “thinking off” is stated to the provider or merely left unsaid. True (the default) sends extra_body.thinking.type: "disabled" on every call made with reasoning switched off – the Worker’s, the validator’s, and the four utility calls (chat title, follow-up suggestions, compaction summary and the LLM routing stage). This matters because saying nothing does not mean off: a reasoning model left to its own default reasons, so a deployment that switched thinking off was still paying for it in first-token latency on every call, including a routing decision that should take a fraction of a second, and discarding the output unread. Note that no reasoning_effort is sent with the disable, because the provider enumerates only [low high xhigh max] and rejects the request outright on anything else – whether is thinking.type, never reasoning_effort. Set False only for a provider that rejects an unrecognised thinking key outright, where stating off would lose every call rather than merely be ignored.
AskJoobyTemperature null The sampling temperature (see ChatOptions.Temperature) sent on the Worker’s own calls (both the first attempt and, if validation rejects it, the corrected retry – never the validator’s, which has its own AskJoobyValidationTemperature). Left blank by default to use the provider’s own default rather than assuming a value suits every model.
AskJoobyMaxFanOut 8 The most independent sub-tasks a single spawn_agents call may fan out to at once (see RunContext.MaxFanOut and SpawnAgentTool) – each one runs as its own concurrent Joobrik run, so this is a real ceiling on simultaneous inference calls from one tool call, not just a suggestion. Generous by default, since the whole point of spawn_agents is trading a wider burst of concurrent calls for less context piling into the caller’s own conversation history. A request for more than this many sub-tasks in one call is refused outright (asking the model to split into smaller batches) rather than silently truncated.
AskJoobyMaxSpawnDepth 6 How many hops of delegation a single turn may nest before a Joobrik stops being handed SpawnAgent/SpawnAgents at all (see RunContext.MaxDepth and AgentFactory.CreateAsync’s canDelegate). Depth 0 is the turn’s own top-level agent; each spawned Joobrik is one deeper than whoever spawned it, however many stables down the chain goes, and a stable’s own sub-specialist is free to spawn further Joobriks of its own as long as this ceiling is not yet reached – delegation is deliberately allowed to nest, not flattened to a fixed number of tiers. Hitting the limit does not throw: the agent at that depth is simply not given the two delegation tools, and its own instructions say plainly that this is the configured ceiling rather than a bug, so it answers directly or hands back instead of wondering where SpawnAgent went. Set lower to bound a runaway delegation chain more tightly, higher to allow deeper nesting; a non-positive or unset value falls back to 6.
AskJoobyMemoryDecayHalfLifeDays 30 Salt Lick recall ranking: on top of the hard ExpiresAt cutoff (see MemoryTool.Remember), a surviving memory’s relevance score is multiplied by an exponential time-decay factor that halves every this-many days since it was recorded (see SearchAskJoobyMemoryQuery’s DecayFactor) – 0.5 ^ (age_in_days / AskJoobyMemoryDecayHalfLifeDays). This keeps an old memory that happens to share keywords/embedding similarity with the query from consistently outranking an equally relevant but fresher one, without a hard age cutoff – a strongly-matching old memory can still surface, just ranked lower than it otherwise would be. An unset or non-positive value falls back to 30.
AskJoobyEnableValidation True Whether every substantive reply gets checked by a second, independent tool-using agent (a fact-checker with its own SearchRag access, checking for claims that are actually wrong or invented – see AgentDefinitions[“validator”]) before it ships, reading every Bite retrieved anywhere in the conversation so far. Deliberately has no gate on the Worker having searched this turn: a reply the Worker wrote without searching at all is exactly the case most worth catching, and the validator searches for what it needs regardless. On a rejection, the failed reply and the validator’s specific critique go back to the Worker itself for one corrected attempt (not the validator rewriting it – the Worker has the real SearchRag/memory access to actually chase down what was flagged); if that retry is still rejected it ships anyway with a visible caveat quoting the remaining concerns. While this is on, the reply text is never streamed to the transcript until it has passed the check (activity items like Steps and Thoughts still stream live, so it never looks stalled) – otherwise the caller would watch a hallucinated first draft appear and then get silently rewritten under them. Defaults to True – proved itself a genuinely useful general safety net during testing (not just a crutch for a weak Worker model). It adds real latency and cost to every reply; set False if that trade-off isn’t worth it for your deployment. Calibrated deliberately away from a “must be verbatim” bar after live testing showed an over-strict validator rejecting accurate paraphrasing as often as it caught real hallucinations – if it starts rejecting almost everything again, that prompt (ResponseValidator) is the first place to look, not this flag.
AskJoobyMaxToolResultCharacters 6000 How large a bound service operation’s result may be, serialised, before it is stored and replaced by a pointer instead of being returned inline (see LargeResultPointer). A tool result stays in the model’s context once it is there, so it is resent on every remaining model call of the turn: a single large listing is paid for repeatedly and can exhaust the context window outright, and some operations return a great deal – WafAttackList alone is capped at a hundred thousand rows. Over the threshold the full result goes to the same cache RunSqlQuery uses and the model is handed a data_pointer, the column list and a short preview, in the same shape a query result arrives in, so InspectDataSchema, PreviewDataRows and QueryCachedData read and filter it exactly as they would a query. The operation itself is unchanged and still returns everything it was asked for; only what travels into the context is reduced. The model is told to filter at the source rather than ask for the whole thing back, so the pointer also teaches it to reach for a narrower call or a better-fitting operation next time. Set lower to be stricter about context, higher to keep more inline; 0 disables the mechanism entirely and restores the previous behaviour of returning every result inline however large. See docs/AskJooby/DataPointerCache.md for the full data_pointer mechanic this paragraph summarises.
AskJoobyMaxSearchesPerTurn 64 How many Pasture searches one turn may make before SearchRag stops returning results and tells the model to answer from what it already has. A backstop against a loop re-querying indefinitely rather than a tight budget: Bites are deduplicated within a turn, so a repeated or reworded search already returns a single line naming what was given earlier instead of the documents again, and an identical search is served from the turn’s repeat ledger without reaching the Pasture at all, which removes most of the cost a cap would otherwise be controlling. Counts every search the turn makes including those made by spawned Joobriks, since they share the turn’s ledger – which is why it was raised from 8: a fan out of five Joobriks could see the first of them spend the whole allowance, leaving its siblings refused on their first search, before a single Bite, with nothing to quote and no way to say why. The real cost is new Bite text in the context, and AskJoobyTokenBudget bounds that directly. Set 0 to remove the cap.
AskJoobyEnableSuggestions True Whether the two or three short follow-up prompts shown under a finished reply are generated. On by default, as they always were. They cost a full round trip to the main chat model on every substantive turn, which is a real per-turn cost for a convenience rather than part of the answer – set False where that trade is not worth it. Only the close of the reply is sent, and nothing else – not the caller’s question, not the middle of a long answer. A finished reply already restates what it was about, and its end is where its conclusion and open threads sit, which is what a follow-up grows from. Suggestions are live only and never persisted, so turning this off changes nothing about stored conversations.
AskJoobyValidationModel deepseek-v4.1-flash The chat model name used for the validator/reviser calls when AskJoobyEnableValidation is True, requested from the same AskJoobyApiEndpoint. Defaults to a DeepSeek model for regional availability: the task is a bounded, structured-output one (STATUS: PASSED/FAILED plus a one-sentence REASON, see ResponseValidator), so what matters is instruction following rather than open-ended generation quality, and the cost of the extra round-trip is held down by AskJoobyValidationThinkingEffort rather than by picking a weaker model. Leave blank to have validation reuse AskJoobyModel instead. Get this id wrong (a model the provider doesn’t actually serve) and every validation call 404s – the validator degrades gracefully on that (ships the unvalidated reply rather than losing it – see RunValidatorSubAgentAsync), so it fails quietly rather than breaking the chat, but validation is then silently doing nothing at all; the failure reason is included directly in the “Double-checking…” step text in the transcript, so a bad id here is easy to catch.
AskJoobyEnableThinking False Whether the Worker and every Joobrik it spawns request the provider’s extended reasoning pass. Off by default, and when off the Worker sends an explicit disable so the provider does not reason anyway. When on, the reasoning streams back as reasoning_content and is shown and persisted as a thinking pass labelled with the agent that produced it. The Worker has no other reasoning tool: this switch is the only way it thinks outside its own plain-text plans.
AskJoobyThinkingEffort 1 Which of the provider’s reasoning_effort buckets the Worker and its Joobriks request when AskJoobyEnableThinking is True: 0=low, 1=high, 2=xhigh, 3=max. The default of 1 matches the provider’s own default band. Ignored while thinking is off.
AskJoobyValidationEnableThinking False Whether the validator/reviser calls request extended reasoning. The Worker has its own switch, AskJoobyEnableThinking, which is separate from this one. Off by default to keep the cost of an elective second opinion down; when it is turned on: its reasoning streams back as reasoning_content and is shown and persisted as a thinking pass labelled Stashoo, who does the checking, the same way the Worker’s own is. This was deliberately withheld while the validator ran on Mistral, which hard-rejects an unrecognised extra_body payload with a 400 – set this to False if AskJoobyValidationModel is pointed at any model that does the same, which turns the reasoning off rather than losing validation altogether.
AskJoobyValidationThinkingEffort 0 Which of the provider’s reasoning_effort buckets the validator requests when AskJoobyValidationEnableThinking is True: 0=low, 1=high, 2=xhigh, 3=max, the same ordinals as AskJoobyThinkingEffort. Deliberately the lowest band: validating a reply is a bounded yes-or-no against text already retrieved, not an open problem, so a long reasoning pass buys nothing and spends exactly the round-trip that making validation elective was meant to avoid. It was 1 while this sentence already claimed it was the lowest, which cost a high reasoning pass on every validated reply for no stated reason. Anything outside 0-3 falls back to high.
AskJoobyValidationTemperature 0.0 The sampling temperature (see ChatOptions.Temperature) sent on every validator/reviser call when AskJoobyEnableValidation is True. Defaults to 0.0 for consistent, repeatable pass/fail judgements on the same reply – a fact-checker should not vary its verdict run to run on an unchanged input. Leave blank to omit Temperature entirely and use the provider’s own default instead.
AskJoobyRagTopK 3 The default Bites SearchRag returns per query when the model doesn’t ask for a specific count, for the Worker’s own searches (the validator uses AskJoobyValidationRagMinScore’s threshold but shares this same default). SearchRag also takes a maxBites tool parameter the model can set per call (1-8) – a narrow lookup can ask for fewer, a broad one for more – but that per-call choice is always clamped to 8 regardless of what’s requested, no matter how low this default is set. Each Bite runs roughly 1,500-1,700 tokens, so a default near that 8-Bite ceiling across a multi-search turn would be the main driver of a turn ballooning and eating the conversation’s own anchor user message; a lower default of 3 keeps a turn’s own tool-calling loop from growing uncontrolled, independent of whatever model is bound to AskJoobyModel, while still letting an individual search flex up to the ceiling when the model judges it’s actually needed. It’s a ceiling, not a guarantee: AskJoobyRagMinScore can return fewer than this if the corpus doesn’t have that many genuine matches. The count is echoed directly in the “Grazed the Pasture…” step narration (e.g. “returned 3 Bites”), so watching that number is the way to check whether the model’s own maxBites choices are behaving sensibly.
AskJoobyRagMinScore 0.013 The minimum fused search score (see SearchAskJoobyRagChunkQuery’s reciprocal-rank-fusion of text + vector search, k=60, candidate window 40) a Bite needs to be returned at all, for the Worker’s own SearchRag calls – added after testing showed the query consistently returning exactly AskJoobyRagTopK results regardless of how well any of them actually matched, since a plain ORDER BY score LIMIT n pads out with whatever’s next in rank order rather than stopping when matches run out. Rough scale: a candidate appearing at the very edge of the 40-row candidate window in only one of the two searches scores 1/(60+40) = 0.01; appearing at rank 1 in both scores 2/61 ≈ 0.033. The 0.013 default filters out only the weakest fringe (roughly: not in the top ~15 of either search) while still being generous. Set to 0 to restore the old no-floor behaviour. This is NOT a raw cosine similarity threshold – the vector side of the search only ever contributes rank, never a raw similarity score, to the fused figure this filters on, so thresholds sized for cosine similarity (commonly quoted around 0.55-0.75 for Qwen embeddings) do not transfer to this scale and would filter out every result if used here directly.
AskJoobyValidationRagMinScore 0.02 The same minimum fused search score as AskJoobyRagMinScore, but for the validator/reviser’s own SearchRag calls when AskJoobyEnableValidation is True. Kept separate and stricter by default – a critic auditing a claim for hallucination should lean on tighter matches than a Worker casting a slightly wider net while first researching an answer. Leave blank to fall back to AskJoobyRagMinScore.
AskJoobyEmbedAllMessages True Whether every AskJooby conversation message gets embedded on insert, not just progressive-compaction summaries. Defaults to True so every turn, across every conversation, is individually searchable via the Hoofprints (SearchHistory) – not only compaction summaries. Set False to embed only compaction summaries instead, trading richer history search for less embedding volume/cost.
AskJoobyReadOnly False Deployment-wide switch putting AskJooby into read-only mode. With this on, no agent is ever offered a tool whose underlying Jube.Service operation is a Write or a Delete – PageAgentToolBinder skips binding it at all rather than binding it and rejecting the call later, the same principle as the per-user permission filtering AgentFactory already applies. Defense in depth rather than the only gate: the deployment’s own middleware independently enforces this at call time regardless of what was offered. False by default, so a deployment that has deliberately configured AskJooby gets the full tool surface its users’ own permissions allow.
AskJoobyChatInputPromptInjectionMainThreshold 0.97 The confidence (0.0-1.0) the Defender ONNX classifier’s “main” head must reach on a caller’s own typed chat message or attachment before AgentGuard blocks it as prompt injection (see docs/AskJooby/AgentGuard.md and AskJoobyInjectionGuard.ResolveChatInputPromptInjectionMainThreshold) – the rule also requires its “aux” head to stay under its own fixed 0.64, per AgentGuard.Onnx’s own DefenderPromptInjectionRule. Read once at startup into AskJoobyInjectionGuardSettings via AskJoobyInjectionGuard.Configure, and used only when a tenant has no AskJoobyAgentGuardRule row for ChatInputPromptInjection with Enabled set to False, which skips the Defender classifier entirely regardless of this threshold. Deliberately liberal rather than the library’s own 0.75 default: the transcript that motivated this setting scored 93.1% on the main head and was still a false positive on an ordinary request, so the shipped default demands near-certainty before blocking a trusted on-prem operator’s own words, at the real cost of letting some subtler, non-regex-matched override phrasing through uncaught – lower it if that trade stops being worth it for your deployment. BlockPromptInjection’s deterministic regex runs unconditionally regardless of this setting, and this never affects secrets detection, the topic boundary, or screening of retrieved RAG/memory/history content, all of which are governed separately.
AskJoobyEnableChatInputTopicBoundary True Whether AgentGuard’s topic-boundary judge (EnforceTopicBoundaryWithLlm, rule llm-topic-boundary) runs against a caller’s own typed chat message or attachment at all (see docs/AskJooby/AgentGuard.md and AskJoobyInjectionGuardSettings.ChatInputTopicBoundaryEnabledByDefault). Off by default: in practice it rejected ordinary, squarely on-platform requests (“build me a c5 tree in r using the demo dataset”) often enough to be unworkable, even with a broadened topic list, because the check is binary with no confidence threshold to loosen the way AskJoobyChatInputPromptInjectionMainThreshold loosens the Defender classifier – the only lever is on or off. Set True for a deployment that wants it back, or set the AskJoobyAgentGuardRule row for TopicBoundaryInput per tenant instead, which still wins when present. Never affects the model’s own reply, which is screened by a separate, always-on topic-boundary check (AskJoobyInjectionGuard.CheckOutputAsync, pipeline askjooby-output-guard) built for a different purpose – keeping AskJooby’s own replies from wandering into being used as a general-purpose chatbot, not refusing a caller’s request.
AskJoobyUtilityEnableThinking False Whether the four short utility calls – the chat title, the follow-up suggestions, the compaction summary and the LLM routing stage – request reasoning. Off by default: each is a bounded, structured task with a known output shape, so reasoning on them is spend with nothing to show for it. Off means nothing is sent and the bound model does whatever it does by default, which is what has always happened; the switch exists so that it is a decision rather than an omission, and so a deployment whose model reasons by default can be handed an explicit low band instead of arguing with it.
AskJoobyUtilityThinkingEffort 0 Which band those four utility calls request when AskJoobyUtilityEnableThinking is True: 0=low, 1=high, 2=xhigh, 3=max. Low, because the only reason to turn this on at all is to hold a model’s own default down.
AskJoobyMaxConcurrentToolCalls 4 Maximum number of catalogued service tool calls an agent run may have in flight at once. Each bound tool call opens its own short-lived database connection so that the agent surface never shares the conversation’s own connection – this caps how many of those can exist together and is what stands between a wide agent fan-out and the Postgres connection budget. Excess calls queue rather than fail. Raising it trades connection headroom for parallelism.
AskJoobyAgentIdleTimeoutSeconds 360 How many seconds a spawned Joobrik may produce nothing at all – no tool call, no reasoning delta, no text – before it is cancelled and its sub-task reported back as unfinished. Measured from the last thing it emitted rather than from when it started, so an agent that is slow but working is never cut off, while one that has genuinely stopped does not hold the whole turn open behind it. The calling agent is told what the Joobrik had got to before it went quiet and carries on with the rest of the reply, rather than the turn failing. 0 disables the watchdog.
AskJoobyMaxIterationsPerRequest 200 The most model-call/tool-call round trips a single agent run (the Worker, any one spawned Joobrik, or the validator/reviser passes) may make while answering one request, passed straight through to HarnessAgentOptions.MaximumIterationsPerRequest / FunctionInvokingChatClient.MaximumIterationsPerRequest. A backstop against a run that keeps re-invoking the same tool (SearchRag chief among them) well past the point of making progress, independent of AskJoobyMaxSearchesPerTurn (which counts searches, not round trips, and is shared turn-wide rather than bounding any one run). Non-positive or unset falls back to 200.
AskJoobyTurnTokenBudget 10000000 The most tokens, input and output together across every model call a single conversation turn makes, including every Joobrik it spawns, before tools stop running and the agent is told to take stock, report what it has established and ask the caller whether it is on the right lines. Counts what the provider actually reports rather than an estimate. Set against the observed distribution on a live instance, where the median turn spends about one hundred and thirty thousand tokens and nine turns in ten stay under a million: ten million therefore sits far above ordinary work and leaves room for a genuinely hard build request that fans out across a dozen Joobriks, having been raised from one million after a turn that was merely thorough was cut off with its writes still unmade. 0 means no budget at all. This is the companion to AskJoobyAgentIdleTimeoutSeconds and catches the opposite failure: an agent that is busy rather than stalled, calling tool after tool and spending the whole time. The turn still answers – the budget withholds tools rather than killing the run, so the work already done is reported instead of thrown away, and it is framed as a check-in rather than a failure because the caller can nearly always unblock it with a single word. It bounds a turn, not a conversation: folding older history down happens between turns, so a turn that spends heavily inside one tool loop is stopped here rather than compacted and continued.
AskJoobyAgentTokenBudget 2000000 The most tokens, input and output together, any single agent may spend on its own model calls before its tools stop running and it is told to take stock. Held alongside AskJoobyTurnTokenBudget and independent of it: the turn figure bounds everything a turn does across every Joobrik it sends out, this one bounds each Joobrik by itself, and a stop happens when either is reached. A single all-up turn figure sits badly with how the work actually runs – one agent that reads widely before writing anything consumes the allowance its siblings needed, and the turn ends with the reconnaissance done and the write never attempted – so this bounds the greedy agent without taking anything from the next one. Each spawned Joobrik is issued a fresh allowance of its own rather than inheriting what its parent has left. 0 means no per-agent budget, leaving only the turn figure.
AskJoobyCachedTokenBudgetWeight 0.1 What a cached input token counts for against AskJoobyTurnTokenBudget and AskJoobyAgentTokenBudget, as a fraction of an uncached one. A prefix the provider already holds is served at a fraction of the price of one it has to read, so charging both alike measures tokens rather than cost and stops a turn that was thorough and cheap as readily as one that was wasteful and expensive – which is the whole of why a long agentic turn gets interrupted when it did not need to be. The cached count is read from whatever the provider reports (cache_read_input_tokens, prompt_tokens_details.cached_tokens and the rest; see PromptCacheUsage), and a provider that reports nothing cached is charged in full, so this setting costs nothing where there is no cache. Set it to the ratio your provider actually bills a cache hit at. 1 charges cached and uncached alike; values at or above 1, or at or below 0, are ignored and treated as 1, since a cached token is never dearer than reading it afresh.
AskJoobyMaxOutputTokens 12288 The most tokens the provider may generate in reply to any single model call, sent on the request itself so the ceiling is enforced by the provider rather than only noticed once the tokens are paid for. Measured against a live instance the average reply is under nine hundred tokens, the ninety ninth percentile under six thousand and the largest ever seen eight thousand four hundred and seventy, so this sits clear of ordinary work and bounds only a single runaway generation. It is the companion to AskJoobyTurnTokenBudget and bounds the other half of the problem: that caps what a whole turn may spend across many calls, this caps what one call may produce. 0 sends no cap and leaves the provider to its own model maximum.
AskJoobyRecordPromptPrefixHash False Whether each model call records a hash of the system message it sent, so a run of calls can be checked for whether the provider was really shown an identical prefix. It exists to diagnose prompt caching and is off by default: a working deployment has no use for it, and it writes a column on every model call. Turn it on while investigating a cache that reports no hits, then off again.
AskJoobyShowPromptCacheShare True Whether a reply’s token line shows how much of its input the provider served from its prompt cache, as the cached count and its share of the input. Off by default because a provider that reports no cache figure at all would otherwise put an unexplained figure on every reply, and a number nobody can act on is worse than no number. The underlying count is recorded on every model call either way, so turning this on later shows the share without losing the history.
EnableAskJoobyCostBanner True Whether a reply’s Stats banner also shows how many Joobriks the turn spawned (and how many distinct Joobrik types among them) and the turn’s reconstituted all-up cost, read fresh off AskJoobyEndpointFee and AskJoobyRerankFee on every reply rather than cached, so a rate edited in either table is live immediately. Off by default, the same reasoning as AskJoobyShowPromptCacheShare above: a model or endpoint with no matching rate row makes the whole cost figure null rather than a confidently wrong partial one, and an unexplained null on every reply is worse than no cost line at all until the tables are populated. Chat and embedding tokens price off AskJoobyEndpointFee (input, cached-input and output rates per model, cached-input priced separately from the rest of InputTokens rather than folded into it); reranking prices off AskJoobyRerankFee, kept as a separate table because a reranker bills per document compared against the query, a different unit from a token. Every reply this is on for also gets one durable row written to AskJoobyTurnCost (unique per turn, by the turn’s own root AskJoobyRun), for reporting on cost across turns later without re-walking the run tree.
EnableWebSearch False The deployment-wide switch for AskJooby’s cross-cutting SearchWeb tool, offered to every agent on the same basis as SearchRag (see AgentDefinitions.BasicResearchTools). Checked alongside AskJoobyWebSearchApiKey at startup: both must be set for the tool to be registered at all, otherwise it is left out of the bound set entirely rather than offered and failing the call at runtime. SearchWeb’s own tool description, not a separate master-prompt rule, is what tells the model to reach for it sparingly – only once the Pasture has shown it has nothing on the question, and only for what the Pasture cannot possibly hold: something that changes outside this platform, an external standard, or general real-world knowledge asked about by name.
AskJoobyWebSearchProvider 1 Which web search provider SearchWeb calls, as a WebSearchProviderType ordinal: 1=Tavily. Unset or unrecognised falls back to Tavily (1). Added so a future provider can be swapped in behind the same IWebSearchClient interface and SearchWeb tool without touching either – only this switch and the registration in Startup.cs change.
AskJoobyWebSearchApiKey null The credential sent as the bearer token to AskJoobyWebSearchApiEndpoint. Required for the tool to be registered at all – see EnableWebSearch. Prefer the [@AskJoobyWebSearchApiKey@] Docker Secrets token (see below) rather than a raw Environment Variable, since this value is a genuine secret.
AskJoobyWebSearchApiEndpoint https://api.tavily.com/search The full URL of the provider’s search route. Given in full, the same reasoning as AskJoobyRerankerApiEndpoint: provider search routes do not share a common path convention the way OpenAI-compatible chat/embeddings endpoints do, so nothing here guesses a suffix.
AskJoobyWebSearchMaxResults 5 The default number of results SearchWeb returns when the model doesn’t ask for a specific count. SearchWeb also takes a maxResults tool parameter the model can set per call, clamped to 10 regardless of this default, the same relationship AskJoobyRagTopK has with SearchRag’s own maxBites parameter. An unset or non-positive value falls back to 5.
AskJoobyWebSearchDepth 2 How thoroughly the provider searches before answering, as a WebSearchDepth ordinal shared across every provider this tool could ever call: 1=Basic, 2=Advanced. Not exposed as a per-call tool parameter – unlike AskJoobyWebSearchMaxResults, this is a deployment-wide cost/quality trade-off (Tavily bills Advanced at twice the credits of Basic) rather than something the model should be deciding call by call. Unset or unrecognised falls back to Advanced (2).
AskJoobyWebSearchAnswerMode 2 Whether the provider also returns its own short synthesised answer alongside the raw results, as a WebSearchAnswerMode ordinal shared across every provider: 0=None, 1=Basic, 2=Advanced. Unset or unrecognised falls back to Advanced (2).
AskJoobyWebSearchRawContentMode 2 Whether a result also carries the full extracted page alongside its short snippet (Tavily’s own extraction switch, include_raw_content), as a WebSearchRawContentMode ordinal shared across every provider: 0=None (snippet only), 1=Text, 2=Markdown. On by default (Markdown) because the short snippet alone is often too little for the model to actually answer from; set to None to fall back to snippet-only results and cut the per-result payload size. Unset or unrecognised falls back to Markdown (2).
EnableOpenCorporates False The switch for AskJooby’s OpenCorporatesTool (SearchCompanyByName, GetCompanyDetails, SearchBeneficialOwners) – KYB company-registry lookups and UBO/beneficial-ownership search against the OpenCorporates API. Unlike SearchRag/SearchWeb this is not offered to every agent: it is bound only inside the case-analyst stable (see AgentDefinitions.Bound and HierarchyDomainOf), since it is a narrow compliance capability rather than something every agent plausibly needs. Checked alongside AskJoobyOpenCorporatesApiKey at startup: both must be set for the tool to be registered at all.
AskJoobyOpenCorporatesApiKey null The OpenCorporates api_token, sent as a query parameter (OpenCorporates does not use a bearer header). Required for the tool to be registered at all – see EnableOpenCorporates. Prefer the [@AskJoobyOpenCorporatesApiKey@] Docker Secrets token (see below) rather than a raw Environment Variable, since this value is a genuine secret.
AskJoobyOpenCorporatesApiEndpoint https://api.opencorporates.com/v0.4 The base URL of the OpenCorporates v0.4 REST API. OpenCorporatesClient appends /companies/search, /companies/{jurisdiction}/{number}, or /statements/control_statements/search itself.
AskJoobyOpenCorporatesMaxResults 5 The default number of companies SearchCompanyByName returns when the model doesn’t ask for a specific count. SearchCompanyByName also takes a maxResults tool parameter the model can set per call, clamped to 10 regardless of this default, the same relationship AskJoobyWebSearchMaxResults has with SearchWeb’s own maxResults parameter. An unset or non-positive value falls back to 5.

Environment Variables passed to Jube at startup will override the hardcoded defaults above. Environment Variables are not written to disk in any form, and their contents are available only in the Jube application’s memory and operating system session.

The Environment Variable string can be tokenised, and secrets can be swapped in from a file topology at a given path, where the file name is the key and the contents of the file is the value. This approach provides full compatibility with Docker Secrets, which mounts secrets as files using tmpfs (memory-mounted file system). For example, given the token [@RedisPassword@], a file called RedisPassword and the contents of that file being localhost, the Environment Variable may be passed as RedisConnectionString=[@RedisPassword@]. At startup the token is detected, extracted and replaced with the contents of the file RedisPassword mounted by Docker Secrets. Tokens that resolve are replaced in place; those that do not are left unchanged and an error is written to STDOUT. This approach ensures that only genuinely secret information is stored in Docker Secrets, with more verbose Environment Variable strings being safe to live outside declared secrets, allowing for clearer intent and greater maintainability. Secrets are processed at startup and before logging, with any errors written to STDOUT rather than the logging library.

This mechanism is entirely free-form: there is no fixed or hardcoded list of recognised token names anywhere in Jube. Any Environment Variable’s string value is scanned for the [@...@] pattern, and whatever name appears between the brackets is looked up directly as a file name under SecretsPath - the token name and the Environment Variable it appears in are otherwise unrelated to each other and to any specific secret Jube ships with. Naming a new secret file and referencing it as [@ThatFileName@] from any Environment Variable is enough to wire it in; no code change is needed to add support for a new token.