Type something to search...
Browse documentation

Environment variable reference

Everything ProPR and ProCursor read at startup comes from the process environment. There is no configuration file to edit; everything else is configured in the management UI.

This page is the only place in these docs that states a default or an accepted range. Other pages name a variable and link here.

How a value can fail to take effect

Three different things happen depending on how a value is wrong, and only one of them is loud:

What you didWhat happens
Set it somewhere the container never seesSilently ignored. See the example stack below.
Set it to something unparsable - a word where a number belongs, or a blank valueSilently ignored; the default is used.
Set it to a parsable value outside the accepted rangeStartup fails for most settings, with a message naming the setting and its bounds.

Three groups behave differently, and it is worth knowing which:

  • The session limits are stricter. MEISTER_SESSION_IDLE_MINUTES and MEISTER_SESSION_ABSOLUTE_HOURS fail startup on anything that is not a positive integer, rather than falling back to their default.
  • The intervals marked “clamped” are looser. A value under the minimum is silently raised to it.
  • The AI_* review-tuning settings are not range-checked at all. An out-of-range value is used as given. The ranges listed for them are the values the review loop is built for, not a guard, and a nonsensical one degrades reviews silently rather than refusing to start.

Other symptoms, and the page that fixes each: troubleshooting.

Does the example stack forward it?

Read this before you put anything in .env and expect it to take effect.

Compose only passes a variable to a container if the service’s environment: block names it. The bundled example/docker-compose/docker-compose.yml names a fixed set. Anything else you put in .env is silently ignored.

One syntax note that applies to every value on this page: in an env file, do not put spaces around the equals sign. KEY=value, never KEY = value - the spaces become part of the name and the value.

The last column of every table below says which:

ValueMeaning
yesThe compose file forwards it from .env
pinnedThe compose file sets a fixed value; .env cannot change it
noNot forwarded. Setting it in .env does nothing.

To use one that is not forwarded, add it to the meisterpropr service’s environment: block yourself:

    environment:
      - AI_ALLOW_PRIVATE_EGRESS=${AI_ALLOW_PRIVATE_EGRESS:-}

Required values

VariableWhat it doesDefaultAcceptedExample stack
MEISTER_JWT_SECRETSigning secret for session access tokensnoneat least 32 charactersyes
MEISTER_BOOTSTRAP_ADMIN_USERUsername of the admin account seeded on first startnone-yes
MEISTER_BOOTSTRAP_ADMIN_PASSWORDPassword for that accountnone-yes
DB_CONNECTION_STRINGPostgreSQL connection string for the ProPR databasenone-yes
PROCURSOR_SHARED_KEYShared secret the API and ProCursor authenticate to each other withnone-yes
PROCURSOR_DB_CONNECTION_STRINGPostgreSQL connection string for the ProCursor databasenone-yes
PROCURSOR_PROPR_BASE_URLInternal base URL ProCursor calls the API onnoneabsolute URLyes

What “required” means differs per variable, and the failure modes are not alike:

  • The bootstrap admin values are read only when no active admin user exists. When one does, they are ignored. When none does and they are absent, startup fails.
  • ProPR registers its database-backed features only when DB_CONNECTION_STRING is set.
  • The ProCursor service refuses to start without its own connection string, its shared key, and the API base URL. On the API side, an absent shared key or service base URL simply turns ProCursor off - see running without ProCursor.
  • MEISTER_JWT_SECRET is different in kind: it is read when the first token is signed, not at startup. A missing or too-short secret therefore lets the stack come up healthy and fails the first sign-in instead.
  • On the bundled compose stack, an absent PROCURSOR_SHARED_KEY does not turn ProCursor off - that stack always defines the service and gates the API on it, so ProCursor crash-loops and the API never starts. See running without ProCursor.

So of the four the example stack asks for up front, three stop the stack from starting and the JWT secret only stops sign-in.

Both secrets should be long random strings. What rotating the JWT secret costs: sign-in and sessions.

Public URL and browser origins

VariableWhat it doesDefaultAcceptedExample stack
MEISTER_PUBLIC_BASE_URLThe externally reachable API base URL, used for webhook listener URLs, SSO redirects and the allowed browser originfalls back to the request hostabsolute URL, including the proxy’s /api prefix if it has oneyes
CORS_ORIGINSExtra browser origins allowed to call the APInonecomma-separated originsyes

See deployment topology for why both matter behind your own ingress.

Encryption key ring

VariableWhat it doesDefaultAcceptedExample stack
MEISTER_DATA_PROTECTION_KEYS_PATHDirectory holding the key ring that encrypts stored provider and AI credentialsunset - keys live on the container’s own filesystem and are lost when it is replacedwritable directory path, created if absentpinned

The example stack pins this to a fixed in-container path rather than reading it from .env, and mounts a named volume there shared by both services. Putting it somewhere durable of your own means editing that line and mounting your volume in its place.

Why this is not optional, why both services take the same path, and what to back up with it: the encryption key ring.

Sessions and sign-in protection

VariableWhat it doesDefaultAcceptedExample stack
MEISTER_SESSION_IDLE_MINUTESIdle timeout, renewed by activity480any positive integerno
MEISTER_SESSION_ABSOLUTE_HOURSAbsolute session lifetime, fixed at sign-in72any positive integerno
MEISTER_AUTH_LOCKOUT_MAX_ATTEMPTSConsecutive failed passwords that trigger a lockout51–100no
MEISTER_AUTH_LOCKOUT_BASE_MINUTESLockout duration at the first threshold151–1440no
MEISTER_AUTH_LOCKOUT_MAX_MINUTESCap on the exponentially backed-off lockout duration601–10080no
MEISTER_AUTH_RATELIMIT_ENABLEDWhether the per-IP limiter on the auth endpoints runstruetrue, falseno
MEISTER_AUTH_RATELIMIT_PERMITSAuth requests permitted per window per client IP201–10000no
MEISTER_AUTH_RATELIMIT_WINDOW_SECONDSLength of that window601–3600no

What these controls are for, and why the per-IP limit is deliberately looser than the account lockout: sign-in and sessions.

Review workers

VariableWhat it doesDefaultAcceptedExample stack
WORKER_MAX_CONCURRENT_REVIEW_JOBSHow many review jobs one API instance runs at once41–64no
WORKER_POLL_INTERVAL_MILLISECONDSHow often the worker looks for pending jobs200010–60000no
WORKER_STUCK_JOB_TIMEOUT_MINUTESHow long a job may sit in processing before it is failed305–1440no

WORKER_MAX_CONCURRENT_REVIEW_JOBS needs a commercial license for parallel review execution to have any effect - see editions - and it is one of the two multipliers described under review workers.

Review workspace

VariableWhat it doesDefaultAcceptedExample stack
REVIEW_WORKSPACE_ROOT_PATHWhere repository mirrors and per-review workspaces are storeda directory under the service account’s local application datawritable directory pathno
REVIEW_WORKSPACE_MAX_CACHE_SIZE_MEGABYTESUpper bound on the whole workspace root4096128–1048576no
REVIEW_WORKSPACE_RETENTION_MINUTESHow long a released workspace is kept before cleanup may remove it1801–10080no
REVIEW_WORKSPACE_MAX_CONCURRENT_PREPARATIONSHow many workspaces may be prepared at once41–128no

Why this wants a mounted volume: review workspace.

Background intervals

VariableWhat it doesDefaultAcceptedExample stack
PR_CRAWL_INTERVAL_SECONDSHow often the crawler polls for open pull requests60clamped to at least 10no
MENTION_CRAWL_INTERVAL_SECONDSHow often ProPR scans for @-mentions to answer60clamped to at least 10no
REVIEW_ARCHIVE_PURGE_INTERVAL_SECONDSHow often the retention purge sweeps archived pull-request content3600clamped to at least 60no

Crawling and @-mention scanning need a commercial license (editions); the purge sweep does not. What the purge deletes and what it never touches: what ProPR stores.

Read by the API to reach ProCursor, and by ProCursor to reach the API.

VariableWhat it doesDefaultAcceptedExample stack
PROCURSOR_REMOTE_MODEproprManagedRemote to use the service, disabled to run without itinferred: remote when a service base URL and shared key are both set, otherwise disabledproprManagedRemote, disabledyes
PROCURSOR_SERVICE_BASE_URLInternal base URL the API calls ProCursor onnoneabsolute URLyes
PROCURSOR_HEALTH_ENDPOINTPath the API probes for ProCursor’s health/healthzpathno
PROCURSOR_REQUEST_TIMEOUT_SECONDSTimeout budget for calls between the two services301–600no
PROCURSOR_RUNTIME_CONFIG_TTL_SECONDSHow long ProCursor caches the runtime configuration it fetches from the API3001 or moreno

ProCursor indexing and queries

Read by the ProCursor service.

VariableWhat it doesDefaultAcceptedExample stack
PROCURSOR_MAX_INDEX_CONCURRENCYConcurrent indexing jobs21–32no
PROCURSOR_MAX_QUERY_RESULTSResults returned per knowledge or symbol query51–20no
PROCURSOR_MAX_SOURCES_PER_QUERYSources scanned for one query201–50no
PROCURSOR_CHUNK_TARGET_LINESTarget chunk size, in lines, for indexed text12010–1000no
PROCURSOR_MINI_INDEX_TTL_MINUTESLifetime of a review-time mini-index overlay301–1440no
PROCURSOR_REFRESH_POLL_SECONDSHow often the indexing worker looks for work301–3600no
PROCURSOR_TEMP_WORKSPACE_RETENTION_MINUTESHow long a stale temporary indexing workspace is kept1201–1440no
PROCURSOR_EMBEDDING_DIMENSIONSExpected embedding vector width; must match the embedding model in use15361–4096no
PROCURSOR_TOKEN_USAGE_ROLLUP_POLL_SECONDSHow often ProCursor aggregates its token usage90010–86400no
PROCURSOR_TOKEN_USAGE_EVENT_RETENTION_DAYSRetention for raw ProCursor token usage events3651–3650no
PROCURSOR_TOKEN_USAGE_ROLLUP_RETENTION_DAYSRetention for aggregated ProCursor token rollups7301–3650no

Review loop budgets

These bound the work one review may do. They are the settings that most directly move token spend that the management UI does not expose - see control cost.

VariableWhat it doesDefaultAcceptedExample stack
AI_MAX_ITERATIONS_LOWInvestigation iterations allowed on a low-complexity file51–100yes
AI_MAX_ITERATIONS_MEDIUMSame, medium complexity101–100yes
AI_MAX_ITERATIONS_HIGHSame, high complexity201–100yes
AI_MAX_REVIEW_ITERATIONSIteration ceiling for review work that is not per-file, where no complexity tier applies201–100no
AI_MAX_FILE_REVIEW_CONCURRENCYHow many files one review works on in parallel31–10no
AI_MAX_FILE_REVIEW_RETRIESRetries for a review job with failed file passes31–10no
AI_MAX_RATE_LIMIT_RETRIESTransparent retries after a provider rate-limit response31–10no
AI_MAX_BACKOFF_SECONDSMaximum backoff between those retries305–120no
AI_FILE_BATCH_LINESLines returned per file-content read the reviewer makes10010–1000no
AI_MAX_FILE_SIZE_BYTESLargest file the reviewer may read; above it the read returns an error instead of content10485761024 or moreno

Which files land in which complexity tier is decided per review - see reviews.

Finding thresholds

VariableWhat it doesDefaultAcceptedExample stack
AI_CONFIDENCE_THRESHOLDConfidence at which the reviewer stops investigating a concern700–100no
AI_CONFIDENCE_FLOOR_ERRORMinimum confidence to post at error severity; below it the finding is downgraded to warning800–100yes
AI_CONFIDENCE_FLOOR_WARNINGMinimum confidence to post at warning severity; below it the finding is downgraded to suggestion600–100yes
AI_QUALITY_FILTER_THRESHOLDTotal comment count across all files below which the cross-file quality pass is skipped201–500yes

These sit under the publication gate, not in place of it - see why a finding did not get posted.

Thread memory

VariableWhat it doesDefaultAcceptedExample stack
AI_MEMORY_TOP_NPast resolutions retrieved per file review31–20yes
AI_MEMORY_MIN_SIMILARITYMinimum similarity for a past resolution to be considered0.800.0–1.0yes
AI_MEMORY_EMBEDDING_DIMENSIONSEmbedding width; must match the model bound to the embedding purpose153664–4096yes

The embedding model itself is configured per client, not here - see purposes.

Code-structure tools

The kill switches exist so a deployment can fall back to simpler behaviour; the budgets bound how much work one lookup may do before it returns what it has, marked truncated.

VariableWhat it doesDefaultAcceptedExample stack
AI_ENABLE_STRUCTURAL_BOUNDARY_RESOLUTIONUse the structural analyzer to pick context boundaries instead of a line heuristictruetrue, falseno
AI_STRUCTURAL_PARSE_TIMEOUT_MSPer-file budget for that analysis before it falls back to the heuristic20010–5000no
AI_MAX_STRUCTURAL_PARSE_BYTESLargest file it will parse5242881024–5242880no
AI_ENABLE_STRUCTURAL_REFERENCE_TOOLSRegister the cross-file reference and definition lookups and the caller-evidence feedtruetrue, falseno
AI_MAX_REFERENCE_CANDIDATE_FILESFiles scanned per reference lookup2001–2000no
AI_MAX_REFERENCE_RESULTSConfirmed sites returned per lookup501–1000no
AI_MAX_REFERENCE_RESULT_CHARSCharacter budget for one lookup result8000256–64000no
AI_REFERENCE_RESOLUTION_TIMEOUT_MSWall-clock budget for one lookup400050–30000no
AI_ENABLE_RETAINED_TOOL_EVIDENCEExperimental. Keeps fetched file content across context compaction instead of dropping it, changing both token profile and review behaviourfalsetrue, falseno

Linked work items and issues

VariableWhat it doesDefaultAcceptedExample stack
AI_ENABLE_LINKED_ITEM_TOOLSWhether the on-demand linked-item lookups are available; false withholds them even from clients that include linked itemstruetrue, falseno
AI_MAX_LINKED_ITEMS_IN_CONTEXTLinked items injected into the review context before the rest are dropped51–50no
AI_MAX_LINKED_ITEM_DESCRIPTION_CHARSLength each linked item’s description is truncated to2000128–20000no
AI_MAX_LINKED_ITEM_TOOL_CALLSOn-demand linked-item lookups allowed per review60–100no
AI_MAX_LINKED_ITEM_TOOL_RESULT_CHARSCharacter budget for one such result8000256–64000no
AI_LINKED_ITEM_TOOL_TIMEOUT_MSBudget for one such lookup; on overshoot it returns empty5000100–30000no

Whether linked items are pulled in at all is a per-client setting - see what you can tune.

Egress and stored content

VariableWhat it doesDefaultAcceptedExample stack
AI_ALLOW_PRIVATE_EGRESSPermit outbound AI calls to private, loopback and link-local addressesfalsetrue, falseno
AI_CAPTURE_REASONING_IN_PROTOCOLRecord the model’s reasoning into the review protocol; it can contain verbatim source excerptstruetrue, falseno

What private egress does and does not permit: outbound request protection. What the captured reasoning contains, and when to turn it off: what ProPR stores.

A process-wide Azure credential

VariableWhat it doesDefaultAcceptedExample stack
AZURE_TENANT_IDMicrosoft Entra tenant of the service principalnone-no
AZURE_CLIENT_IDApplication ID of the service principalnone-no
AZURE_CLIENT_SECRETIts client secretnone-no

All three together supply one Azure service principal to the backend process. That one credential serves two unrelated purposes: Azure DevOps operations for a client that has no connection of its own - see global Azure fallback - and Azure-hosted AI endpoints configured with Azure Identity instead of a key.

Set fewer than three and none of them are used. When they are all absent, ProPR uses the ambient Azure credential instead: a managed identity, an Azure CLI login, or whatever else the host offers.

Observability

VariableWhat it doesDefaultAcceptedExample stack
OTLP_ENDPOINTOTLP collector to export traces tonone - no trace pipeline is built at all, so spans are never assembledabsolute URLno
TELEMETRY_HTTP_CLIENT_TRACESWhich outbound requests become spans. foreground skips unattended work: crawl cycles, mention scans and health probesforegroundforeground, all, offno
TELEMETRY_TRACE_SAMPLE_RATIOHead-sampling ratio applied to the traces that survive the filters1.0 - no sampler is installed, which leaves the standard OTEL_TRACES_SAMPLER and OTEL_TRACES_SAMPLER_ARG in charge0.0 to 1.0, clamped into rangeno
TELEMETRY_TRACE_IGNORED_PATHSRequest path prefixes that are never traced, inbound or outbound/healthz,/livez,/metricscomma-separated path prefixes, leading / optionalno
LOKI_URLGrafana Loki instance to ship logs tonone - logs go to stdout onlyabsolute URLpinned
ASPNETCORE_ENVIRONMENTThe runtime environment nameProduction when unsetProduction, Development, or your own namepinned

The three TELEMETRY_ variables only do anything while OTLP_ENDPOINT is set, and none of them affect /metrics, which keeps counting every request either way. An unrecognised value is not an error: the trace mode falls back to foreground and an unparseable ratio to 1.0, so a typo silently gets you the default rather than a failed start. Why you would change them: trace volume.

Development is not a production setting: it serves the API documentation UI - see the API reference - and relaxes the outbound AI egress checks - see outbound request protection. Run Production anywhere real. Where traces and logs end up: observability.

An optional Azure OpenAI instruction evaluator

VariableWhat it doesDefaultAcceptedExample stack
AI_EVALUATOR_ENDPOINTAzure OpenAI endpoint for the repository-instruction relevance evaluatornoneAzure OpenAI endpoint URLno
AI_EVALUATOR_DEPLOYMENTDeployment name to call on itnone-no
AI_API_KEYKey for that endpoint; omit to authenticate with the host’s ambient Azure credential insteadnone-no

This evaluator judges which of a repository’s instruction files apply to a diff. It is registered only when the endpoint and the deployment are both set, and it is Azure OpenAI only - it does not go through the per-client AI connections, so it is the one model call in the product that a client’s provider configuration does not control. Leave all three unset unless you specifically want it.