For the complete documentation index, see llms.txt. This page is also available as Markdown.

Configuration Reference

Every eddi.* property EDDI reads, with its default and the environment variable that sets it. This is the deployment/operator surface — agent behaviour is configured as JSON documents, not here. If you are trying to change what an agent says or does, you want Behavior Rules, LLM Integration or Properties instead.


How to set these

Three interchangeable mechanisms, in ascending order of precedence:

Where
Form
Use it for

src/main/resources/application.properties

eddi.schedule.poll-interval=15s

Source builds and the shipped defaults

Environment variable

EDDI_SCHEDULE_POLL_INTERVAL=15s

Docker, Compose, Kubernetes — the normal path

JVM system property

-Deddi.schedule.poll-interval=15s

One-off local overrides

The environment-variable spelling is mechanical: uppercase the property name and replace every character that is not a letter or a digit with _. Both . and - are replaced — neither is dropped.

eddi.schedule.poll-interval         →  EDDI_SCHEDULE_POLL_INTERVAL
eddi.vault.master-key               →  EDDI_VAULT_MASTER_KEY
eddi.openai-compat.api-key          →  EDDI_OPENAI_COMPAT_API_KEY
eddi.tools.websearch.google.api-key →  EDDI_TOOLS_WEBSEARCH_GOOGLE_API_KEY

Getting this wrong fails silently. An unrecognised environment variable is not an error — the property simply keeps its default and the service starts normally. EDDI_VAULT_MASTERKEY (dash deleted rather than replaced) leaves eddi.vault.master-key empty, which means the vault is inactive, and a scope: "secret" property setter then fails the whole turn. Nothing in the startup log mentions the variable you set.

To check what actually bound, read the value back from the Dev UI at /q/dev, or compare against the spellings already used in docker-compose.yml, .env.example and k8s/.

Quarkus profiles prefix the key: %dev.eddi.usermemories.deleteOlderThanDays=-1 applies in dev mode only.

Empty is not the same as unset. Several properties ship deliberately empty (eddi.vault.master-key, eddi.deployment.env). An empty value is a real value that disables or defaults the feature; it is not an error, and startup will not complain.


Data store & messaging

Property
Default
Description

eddi.datastore.type

mongodb

mongodb or postgres. Selects the whole persistence layer — see Architecture → DB-agnostic design

eddi.messaging.type

in-memory

in-memory or nats. in-memory confines the conversation coordinator to a single instance; nats distributes it

NATS JetStream (only when eddi.messaging.type=nats)

Property
Default
Description

eddi.nats.url

nats://localhost:4222

Server URL

eddi.nats.stream-name

EDDI_CONVERSATIONS

JetStream stream carrying conversation work

eddi.nats.dead-letter-stream-name

EDDI_DEAD_LETTERS

Stream that receives messages past max-retries

eddi.nats.max-retries

3

Redelivery attempts before dead-lettering

eddi.nats.ack-wait-seconds

60

How long JetStream waits for an ack before redelivering. Must exceed your slowest conversation turn, or slow turns are processed twice


Conversation lifecycle & retention

Property
Default
Description

eddi.conversations.maximumLifeTimeOfIdleConversationsInDays

90

Idle conversations are closed after this many days

eddi.conversations.deleteEndedConversationsOnceOlderThanDays

365

Ended conversations are permanently deleted after this many days

eddi.conversations.max-input-chars

200000

Longest turn input, in characters, a caller may send to an existing conversation. Longer input is refused before anything reaches the model: 413 input_too_large on the REST and streaming conversation endpoints, 400 input_too_large on the OpenAI-compatible API, invalid params over A2A; other surfaces built on those entry points report the refusal as an error. 0 or negative disables the limit. Turns the engine drives itself for group members and sub-agents are exempt

eddi.usermemories.deleteOlderThanDays

-1

Persistent user memories older than this are deleted. -1 disables the sweep — memories are kept forever until you set a positive number. Relevant to GDPR and HIPAA

eddi.coordinator.max-active-conversations

10000

Ceiling on concurrently tracked conversations

eddi.coordinator.max-dead-letters

1000

Retained dead-letter entries. -1 unbounded, 0 retain none

Graceful shutdown

Tuned for Kubernetes rolling updates: EDDI reports itself not-ready first, waits for the load balancer to notice, then drains in-flight conversations.

Property
Default
Description

eddi.shutdown.readiness-grace-seconds

3

Time between failing readiness and starting the drain. Must exceed your ingress's health-check interval, or requests keep arriving mid-drain

eddi.shutdown.drain-timeout-seconds

20

How long to wait for in-flight conversations before exiting anyway. Keep it below your orchestrator's terminationGracePeriodSeconds

eddi.shutdown.drain-poll-millis

100

How often the drain re-checks for completion

Streaming

Property
Default
Description

eddi.streaming.cancel-on-client-disconnect

true

Abort the turn when an SSE client goes away. Set false to let the turn finish and persist, so a reconnecting client can read the result

eddi.llm.tool-loop.streaming.enabled

true

Stream tokens during tool-calling turns rather than falling back to a single chunk


Scheduling

Full narrative and metrics: scheduling.md → Deployment Configuration.

Property
Default
Description

eddi.schedule.enabled

true

Master switch for the poller

eddi.schedule.poll-interval

15s

How often due schedules are looked for — the floor on firing punctuality

eddi.schedule.poll-batch-size

100

Schedules claimed per cycle

eddi.schedule.lease-timeout

5m

Claim lease before another instance may re-claim. Set above your slowest fire, or slow runs execute twice

eddi.schedule.max-retries

5

Attempts before DEAD_LETTERED

eddi.schedule.backoff-base-seconds

15

Retry delay = base × multiplier^(attempt-1)

eddi.schedule.backoff-multiplier

4

Defaults give 15s, 60s, 4m, 16m, 64m

eddi.schedule.min-interval-seconds

60

Smallest cron interval a schedule may request

eddi.schedule.instance-id

(hostname)

Cluster claim identity. Set explicitly where hostnames are recycled

eddi.schedule.default-timezone

UTC

IANA zone for schedules that name none

eddi.schedule.fire-timeout

5m

How long one conversation fire may run before it is abandoned as failed. Keep it at or below lease-timeout — past the lease another instance may reclaim the schedule regardless

eddi.schedule.fire-log-retention

90d

Fire logs older than this are deleted by a periodic sweep. 0 keeps everything — a 60-second heartbeat alone writes ~525,600 rows a year

eddi.schedule.fire-log-prune-interval

1h

How often that sweep runs. The DELETE is by timestamp and therefore idempotent, so it needs no cluster claim


Security & authentication

Authentication itself is Quarkus OIDC, not an eddi.* property: quarkus.oidc.tenant-enabled (default false) is the master switch, and authorization.enabled tracks it so @RolesAllowed is enforced exactly when OIDC is on. See security.md.

Property
Default
Description

eddi.security.allow-unauthenticated

false

Permits running with OIDC disabled outside dev. AuthStartupGuard refuses a production boot without it

eddi.security.ssrf-protection.enabled

false

Opt-in. Validates the fully resolved target of httpCalls, MCP and A2A calls and stops following redirects. Off by default because configured targets legitimately reach internal hosts — turn it on if any outbound URL is influenced by conversation input. The cloud instance-metadata service (and the link-local range it lives in) is refused regardless of this setting. See security.md → SSRF Protection

eddi.mcp.allow-unauthenticated

false

Exposes the MCP server without auth. Needs its own opt-in on top of eddi.security.allow-unauthenticated — inheriting one flag must not be enough to open agent CRUD

eddi.secretstore.allow-unauthenticated

false

Same, for the secrets vault REST surface

eddi.caller-identity.enabled

true

Enables ${caller:token} / ${caller:userId} in httpCall headers. See httpcalls.md

eddi.keycloak.public.url

(empty)

Browser-facing Keycloak URL when it differs from the in-cluster one

Workspaces & resource sharing

Full guide: workspaces.md.

Enforcement and ownership are deliberately separate switches. Ownership is stamped on every new resource whenever authorization.enabled is on, regardless of eddi.workspaces.enabled, so a deployment can run a release with attribution recorded and nothing filtered, confirm the data looks right, and only then enforce. Enforcing before ownership has been stamped and backfilled is what would hide people's own work from them.

Property
Default
Description

eddi.workspaces.enabled

false

Whether workspace access is actually enforced: listings filtered, and reads, writes, deletes and re-sharing each checked against the level the caller holds (VIEW / USE / EDIT / OWN). No effect while authorization.enabled=false — with no authenticated principal there is nothing to scope to, and startup warns rather than denying everyone everything

eddi.workspaces.groups-claim

groups

The JWT claim listing the caller's teams. Needs a group-membership protocol mapper on the eddi-backend client; without one every user simply gets a personal space and no teams, which is a correct answer rather than a failure

eddi.workspaces.legacy-visibility

shared

Who may see resources with no recorded owner: shared (everyone, so an upgrade hides nothing) or admin-only (only eddi-admin, so an operator migrates deliberately and then closes the door). Any other value fails startup rather than picking a policy by guessing

eddi.workspaces.default-space

(empty)

Where new resources are filed. Empty means the creator's personal space; a Keycloak group name gives a team-first deployment, where colleagues see each other's work by default and personal spaces are reached by explicitly moving a resource

Secrets vault

Full guide: secrets-vault.md.

Property
Default
Description

eddi.vault.master-key

(empty)

KEK source. Empty means the vault is inactive. A scope: "secret" property setter then scrubs the plaintext, logs an ERROR and fails the turn with a LifecycleException naming EDDI_VAULT_MASTER_KEY — it never persists the value. (AgentSetupService's own vaultApiKey path is the exception and still degrades; see secrets-vault.md.)

eddi.vault.grant-enforcement

enforce

off, warn or enforce. An unrecognised value fails startup rather than silently disabling the check

eddi.vault.cache-ttl-minutes

5

Resolved-secret cache lifetime

eddi.vault.cache-max-size

1000

Resolved-secret cache entries

eddi.setup.vault-key-reuse

checksum

checksum reuses an existing vault entry when the value matches; never always writes a new one. A typo fails startup

eddi.setup.llm.log-conversation-content

false

Log conversation content during agent setup. Leave off outside debugging

Compliance gates

Property
Default
Description

eddi.compliance.audit-signing-required

false

Refuse to start unless audit HMAC signing is active

eddi.compliance.database-encryption-acknowledged

false

Operator attestation that encryption-at-rest is configured — see hipaa-compliance.md


Audit ledger

Full guide: audit-ledger.md. Note there is no retention property — the ledger is append-only by design; see gdpr-compliance.md.

Property
Default
Description

eddi.audit.enabled

true

Master switch

eddi.audit.flush-interval-seconds

3

Batch flush cadence

eddi.audit.max-queue-size

100000

In-memory queue. When full, entries are dropped and counted by eddi_audit_entries_dropped_total — alert on it, because a non-zero value means the trail has holes

eddi.audit.dead-letter-path

/opt/eddi/data/eddi-audit-deadletter.jsonl

Where undeliverable entries are written. Must be on a persistent volume, or dropped entries vanish with the container

eddi.audit.agent-signing-enabled

true

Sign agent configurations for provenance

eddi.audit.verify.recover-legacy

true

Accept pre-HMAC rows during chain verification

eddi.audit.verify.recover-legacy-max-rows

500

Cap on how many such rows are tolerated


GDPR / CCPA

Full guide: gdpr-compliance.md.

Property
Default
Description

eddi.gdpr.restriction-cache-ttl-seconds

0

How long an Art. 18 restriction verdict may be reused without re-reading the store. 0 — the default — switches the cache off, so every check reads the store. The cache is node-local with no cross-node invalidation, so a cached "not restricted" on one node keeps a restricted user being processed for the length of the TTL after another node applies the restriction, and keeps answering from cache through a store outage instead of failing closed. Raise it only on a single-node deployment or one with conversation affinity


Human-in-the-Loop

Full guide: hitl.md.

Property
Default
Description

eddi.hitl.tool.enabled

true

Per-tool-call approval gating

eddi.hitl.tool.task-approvals.mode

strict

strict unions requireApproval patterns, ignores task-level exempt, and demotes task-level AUTO_APPROVE — a task cannot loosen its agent's gate. replace restores pre-6.3.0 behaviour where a task config fully replaces the agent gate

eddi.hitl.tool.journal-retention

30d

How long tool-approval journal entries are kept

eddi.hitl.tool.transcript-max-bytes

2000000

Cap on a stored approval transcript

eddi.hitl.pending.max-age

(empty)

Auto-cancel pending approvals older than this ISO-8601 duration. Empty = never auto-cancel, so approvals wait indefinitely

eddi.hitl.pending.sweep-interval

6h

How often the auto-cancel sweep runs

eddi.hitl.crash-recovery.enabled

true

Restore paused conversations after a restart

eddi.hitl.crash-recovery.recover-in-progress

true

Also recover turns that were mid-execution

eddi.mcp.hitl.mutations.enabled

true

Allow approve/reject decisions through the MCP surface


Tools & outbound calls

Property
Default
Description

eddi.tools.budget.enforce-by-default

false

Enforce per-conversation tool cost ceilings without a per-task enforceBudget flag. See langchain.md

eddi.tools.ratelimit.global.enabled

false

Deployment-wide tool rate limit, on top of per-tool limits. Both must admit a call

eddi.tools.ratelimit.global.limit

1000

Calls per minute when the above is on

eddi.tools.websearch.provider

duckduckgo

duckduckgo (no key) or google

eddi.tools.websearch.google.api-key

(empty)

Required for the google provider

eddi.tools.websearch.google.cx

(empty)

Google Programmable Search engine ID

eddi.tools.weather.openweathermap.api-key

(empty)

Required by the weather tool

eddi.httpcalls.default-timeout-millis

30000

Per-call timeout when the httpCall does not set one. Without it a call can occupy the conversation thread indefinitely

eddi.httpcalls.default-max-response-size-bytes

2000000

Response-body ceiling. Deliberately above the memory cap, so an over-long body is truncated into memory rather than failing the turn

eddi.mcpcalls.default-rate-limit

100

Default per-minute limit for MCP tool calls

eddi.ollama.default-base-url

http://localhost:11434

Used when an Ollama LLM config omits baseUrl


Attachments

Full guide: attachments-guide.md.

Property
Default
Description

eddi.attachments.max-size-bytes

20971520 (20 MB)

Largest single upload

eddi.attachments.max-per-turn

5

Attachments per turn — per member turn in a group conversation

eddi.attachments.max-per-conversation

50

Attachments per conversation

eddi.attachments.max-total-bytes-per-conversation

104857600 (100 MB)

Aggregate bytes per conversation

eddi.attachments.max-forward-bytes

10485760 (10 MB)

Per-file ceiling on what is forwarded to the LLM, across every source

eddi.attachments.max-forward-aggregate-bytes

20971520 (20 MB)

Aggregate ceiling for one message

eddi.attachments.extraction.max-chars

50000

Cap on text extracted from a document

The upload cap and the forward cap are different numbers on purpose: a 20 MB PDF may be stored and read on demand via the readAttachment tool without being inlined into every prompt.


Backup, export & import

Full guide: import-export-an-agent.md.

Property
Default
Description

eddi.backup.export.retention-minutes

60

How long a finished export archive stays downloadable

eddi.backup.export.sweep-interval

15m

How often the retention sweep runs on its own, independently of exports

POST /backup/export/{agentId} writes a ZIP under tmp/archives/ and answers with a Location header the client then GETs, so the file has to outlive the request. Nothing else deletes it: the sweep runs before every export and on the interval above, so an instance that stops exporting still reclaims what it already wrote. It also removes the loose tmp/*.zip archives earlier releases left behind, which are no longer downloadable. Raise the retention if a client may take longer than that between the POST and the GET; lower it to bound disk use on an instance that exports on a cron.


Protocols & integrations

MCP

Property
Default
Description

eddi.mcp.tool-cache.ttl-ms

300000 (5 min)

How long a remote server's tool list is cached. Lower it while developing against a changing MCP server

eddi.mcp.tool-description.max-chars

1024

Truncation cap on imported tool descriptions, bounding prompt cost

A2A

Property
Default
Description

eddi.a2a.enabled

true

Serve the A2A endpoints

eddi.a2a.base-url

http://localhost:7070

The URL advertised in Agent Cards. Wrong here means peers cannot reach you

eddi.a2a.capabilities.public

false

Serve /.well-known/capabilities unauthenticated

eddi.a2a.tool-description.max-chars

1024

Truncation cap on peer tool descriptions

eddi.a2a.signing.nonce.max-age-ms

300000 (5 min)

Replay window for signed requests

eddi.a2a.signing.nonce.clock-skew-ms

30000

Tolerated clock difference between peers

eddi.a2a.task-timeout-seconds

systemRuntime.agentTimeoutInSeconds

How long a peer's tasks/send may wait for the turn. Inherits the REST surface's budget, because an operator who raised that has already decided how long a turn may take

Slack

Full guide: slack-integration.md.

Property
Default
Description

eddi.slack.request-timeout-seconds

60

How long a single agent turn may take before Slack is told it timed out. A turn that legitimately runs longer — a multi-step tool call, a slow provider, a cascade escalation — needs this raised, and answers with a timeout-specific notice naming the limit rather than a generic error

eddi.slack.group-completion-timeout-seconds

300

How long a whole group discussion may take before follow-up routing gives up

eddi.slack.api-max-retries

3

Attempts, including the first, for a Slack Web API call

eddi.slack.api-retry-base-ms

500

Base delay for the exponential backoff between those attempts

OpenAI-compatible API

Full guide: open-webui-integration.md.

Property
Default
Description

eddi.openai-compat.enabled

false

Serve /v1

eddi.openai-compat.api-key

(empty)

Shared key clients present

eddi.openai-compat.http-policy

permit

permit accepts the shared key; authenticated has Quarkus OIDC validate per-user tokens instead

eddi.openai-compat.trust-user-headers

true

Believe X-OpenWebUI-User-Id as the EDDI userId. Safe only because the caller proved possession of the shared key — a leaked key therefore permits impersonating any user

eddi.openai-compat.allow-anonymous

false

Serve requests carrying no user identity

eddi.openai-compat.default-user

openai-anonymous

userId used when anonymous is allowed

eddi.openai-compat.environment

production

Deployment environment agents are resolved from

eddi.openai-compat.expose-stateless-variants

true

Also list …-stateless model ids

eddi.openai-compat.model-cache-seconds

30

How long /v1/models is cached

eddi.openai-compat.max-concurrent-requests

64

Concurrency ceiling for the adapter

eddi.openai-compat.request-timeout-seconds

120

Per-request timeout

Connections

Full guide: connections.md.

The first four are runtime settings: an administrator changes them without a restart through PUT /connectionstore/settings (or the Manager's Connections page). Setting the property pins the value instead — it wins over the stored one, and the endpoint refuses to change it (409). Unset and unstored, each takes the default below. See Enabling connections.

Property
Default
Description

eddi.connections.enabled

false

Master switch for the connection credential model. Runtime setting enabled

eddi.connections.public-base-url

(none)

Externally reachable base URL for OAuth redirect URIs. Runtime setting publicBaseUrl. A pinned value that is not a bare https origin refuses the boot; without one, per-user account linking answers 400

eddi.connections.credential-endpoint-allowlist

(empty)

Origins that may receive the client secret — a connection's token and authorization endpoints, and only those (RFC 9728 resource-metadata discovery is not implemented). Not where the access token goes: that is each connection's own baseUrlAllowlist. Runtime setting credentialEndpointAllowlist

eddi.connections.allow-plaintext-remote-origins

false

Whether a connection's baseUrlAllowlist may send its credential over plaintext http:// to a non-loopback host. While false such an origin is refused at save time (400), refused per request (TARGET_NOT_ALLOWED) and reported at ERROR at boot; true accepts it with a WARN. Loopback http:// is always allowed. Runtime setting allowPlaintextRemoteOrigins. Upgrade note: existing connections with a remote http:// origin stop resolving until this is set

eddi.connections.settings.allow-unauthenticated-writes

false

Whether PUT /connectionstore/settings accepts a write with no verified identity. Outside dev and test, while authorization.enabled=false, such a write is refused (403) unless this is true@RolesAllowed is a no-op without OIDC, and an anonymous caller must not be able to approve a credential endpoint. Not a runtime setting

eddi.connections.state-sweep-interval

1h

How often expired OAuth state entries are cleared

Upgrade note. Any of the first four properties that is set — even to its default, e.g. EDDI_CONNECTIONS_ENABLED=false copied from an old example — now pins that value, and the settings page shows it read-only. Unset it to let administrators manage the value at runtime.


Groups, tenancy & variables

Property
Default
Description

eddi.groups.max-depth

3

Nesting limit for groups-of-groups

eddi.groups.cadence.claim-ttl

PT24H

How long a standing-team cadence claim is held

eddi.tenant.default-id

default

Tenant assigned when a request names none

eddi.tenant.quota.enabled

false

Per-tenant quota enforcement

eddi.tenant.quota.max-conversations-per-day

-1

-1 = unlimited

eddi.tenant.quota.max-agents-per-tenant

-1

-1 = unlimited

eddi.tenant.quota.max-api-calls-per-minute

-1

-1 = unlimited

eddi.tenant.quota.max-monthly-cost-usd

-1

-1 = unlimited

eddi.variables.cache-ttl-minutes

2

Global variable cache lifetime

eddi.deployment.env

development (when unset)

Value matched by the deploymentContext behavior-rule condition, letting one agent behave differently per environment


Logging & documentation surfaces

Property
Default
Description

eddi.logs.buffer-size

10000

In-memory ring buffer backing /administration/logs

eddi.logs.db-enabled

true

Also persist logs to the database

eddi.logs.db-flush-interval-seconds

5

Persistence batch cadence

eddi.logs.db-persist-min-level

WARN

Minimum level persisted. Lowering this to DEBUG in production will fill the database quickly

eddi.docs.enabled

true

Serve EDDI's own docs at /administration/docs, as MCP resources (eddi://docs/*) and as the list_docs/read_docs MCP tools. One switch covers all three. The content is the public repository documentation, so this is an exposure policy, not a secrecy control

eddi.docs.path

docs

Directory the above is served from


Migration

These run once against an existing database and then stay off.

Property
Default
Description

eddi.migration.v6-rename.enabled

false

Rewrite v5 resource URIs to v6 spellings

eddi.migration.v6-qute.enabled

false

Convert Thymeleaf templates to Qute

eddi.migration.backupBeforeWrite

true

Snapshot documents into .history collections before rewriting. Leave this on

eddi.migration.skipConversationMemories

false

Skip conversation memories, which are the bulk of the data and rarely need rewriting


See also

  • Metrics & Monitoring — what to watch once these are set

  • Security — the reasoning behind the security defaults

  • Kubernetes — how these map onto Helm values and ConfigMaps

  • .env.example — the Docker Compose subset, ready to copy

  • src/main/resources/application.properties — the shipped defaults, with inline commentary

Last updated

Was this helpful?