Documentation / Exploitation / Référence / Environment variables

Environment variables

This page lists every environment variable read by the Meridian runtime processes, the process(es) that read each one, its role, and its default. It mirrors the code; where a variable's behaviour matters, the source file is cited.

The proxy reads its configuration once at boot (readApiConfig, apps/api/src/server/config.ts), and the endpoint reads its own (readEndpointConfig, apps/api/src/restate/config.ts). A variable that is unset or empty takes its default. A malformed value (a port, a duration, a boolean, a URL, an unknown mode) stops the boot with a message that names the variable and the expected form, for example SHUTDOWN_TIMEOUT_MS invalide : « 10s ». Attendu un nombre entier de millisecondes entre 1 et 2147483647.

The processes referred to below are:

Process Entry point Log/service name Default PORT
proxy (runtime API) apps/api/src/server/api.ts meridian-proxy 4000
endpoint (Restate services) apps/api/src/restate/server.ts meridian-endpoint 9080
ui (ehr-lab mini-EHR) apps/ehr-lab (Next.js) — 3000
hospital-sim (simulator) apps/hospital-sim/src/main.ts system:* SIM_PORT = 3300

For the deployment topology these processes form, see Topology.

.env loading

Both proxy and endpoint import apps/api/src/env.ts first. It reads a .env file at the repository root (path resolved relative to the module) if one exists, and copies each KEY=value line into process.env only when the key is not already set — an explicit export FOO=bar in the shell always wins (apps/api/src/env.ts). Lines starting with # and malformed keys are ignored; surrounding single or double quotes on the value are stripped. There is no dotenv dependency.

The ehr-lab app loads the same repository-root .env through its own loader (apps/ehr-lab — src/infrastructure/env.ts).

Consequences:

  • In a checkout, copy .env.example to .env at the repo root and restart the process after any change.
  • In a container/Kubernetes deployment, inject variables through the service environment (compose env_file/environment, or a Secret) rather than copying a .env. Instance manifests are version-controlled and must never contain secrets — see Security posture.

Instance and plugin resolution

Read by both proxy and endpoint at boot.

Variable Process(es) Role Default
INSTANCE_CONFIG proxy, endpoint Path to the instance manifest. Relative paths resolve from the repo root. Absent → instance.yaml at the repo root; if that is also missing, an empty instance (all-internal/in-memory dev default). A path that is set but missing fails the boot loudly. instance.yaml
PLUGINS_PATH proxy, endpoint Extra roots holding external compiled plugins, split on , or the OS path delimiter. Relative entries resolve from the process cwd. (empty)
MERIDIAN_REGISTRY proxy, endpoint Default plugin registry used to resolve instance-declared plugins when the manifest has no registry: key. Accepts gs://bucket[/prefix], file://…, or a repo-relative directory. (none)
MERIDIAN_PLUGIN_CACHE proxy, endpoint Local cache directory for plugins downloaded from the registry; the resolution of the plugins: ranges reads the manifests of the versions already there before the registry. Relative paths resolve from the repo root. .data/plugins

The manifest's own registry: key takes precedence over MERIDIAN_REGISTRY; a plugin listed with a local path bypasses the registry entirely (apps/api/src/registry.ts). A declared plugin that cannot be resolved fails the boot. See Set up a plugin registry, Declare and install plugins, and the Instance manifest reference.

Restate wiring

Variable Process(es) Role Default
RESTATE_URL proxy Restate ingress URL (http:// or https://). Required — the proxy throws at startup if it is unset, because execution runs through Restate. A trailing slash is stripped. (required)
RESTATE_ADMIN_URL endpoint Restate admin API used for auto-registration (POST {admin}/deployments), http:// or https://. A trailing slash is stripped. http://localhost:9070
RESTATE_ENDPOINT_URL endpoint URI under which the Restate server reaches this endpoint (advertised at registration), http:// or https://. http://host.docker.internal:<endpoint PORT>
RESTATE_AUTO_REGISTER endpoint false or 0 disables auto-registration; true or 1 keeps it enabled. (enabled)
API_INTERNAL_URL endpoint Where the endpoint reaches the REST proxy to ingest schedule ticks (trigger.schedule, service meridianScheduler), to read agents and to read and write the workflow memory (/api/internal/memory/*, see Workflow memory storage). http:// or https://, a trailing slash is stripped. Must point at the proxy in any multi-container deployment, or planned workflows never start and memory nodes fail. http://localhost:4000
RUN_JOURNAL_FORMAT proxy Journal format of the runs the proxy starts: 1 or 2, any other value throws at startup. Logged at boot and recorded per run in the run index (journal_format). Set 2 once every endpoint replica runs a release that knows format 2; set 1 again before rolling the endpoint back below it. See Upgrade the platform — Journal formats. 1
AGENT_REFRESH_MS endpoint Period, in milliseconds, at which the endpoint re-reads the store's agents from the proxy; on a change it rebuilds its catalogue, and a failed or empty read keeps the current list (apps/api/src/restate/endpoint.ts). An integer from 0 to 2147483647; 0 disables the refresh. 30000

The endpoint (re)registers its deployment on every start, idempotently (force: true) and with retry (up to ~3 min), so a manual registration step is not needed and the endpoint may start before the Restate server (apps/api/src/restate/register.ts). Registration succeeds only when Restate's discovery response contains every service bound by this endpoint: meridianWorkflow, record, meridianScheduler, meridianWorkflowAssist and meridianWorkflowTests. During a rolling update, the shared endpoint address can still reach an older pod; an HTTP success with missing services logs enregistrement incomplet (including missing) and is retried within the same budget. Creating or editing a workflow never requires re-registration. See Restate integration and Why durable execution.

Run index (run store)

Read by the proxy.

Variable Process(es) Role Default
RUN_DB_URL proxy Backend for the persistent run index and the credentials. sqlite:<path> (local / single node) or postgres://… / postgresql://… (deployment / multiple proxy replicas). An unrecognised prefix throws. sqlite:~/.meridian/runs.sqlite
RUN_SETTLE_INTERVAL_MS proxy Period, in milliseconds, of the settlement pass: the proxy reads how the invocation of each live run of the index ended in Restate, and freezes the runs of its Restate that ended — killed, or completed while nobody read them (see Run index — Final status). 0, or an integer from 1000 to 2147483647. 0 disables the pass: a killed run is then frozen only when its last state is suspended or waiting and its detail is read, a decision is sent for it or its wait is resumed; a run killed while running is never frozen. 60000
DEFINITION_STORE_URL endpoint, proxy Required (postgres://…): the workflow/agent definition store; the same database holds the EAI exchanges and the workflow memory. Falls back to RUN_DB_URL when that one is Postgres — the tables are disjoint, one database is enough. Boot fails with an explicit message if neither is set. See Manage workflow storage.
DEFINITION_SEED_DIR proxy Directory of the host's system definitions, imported as published v1 when the store is empty (default: apps/api — it carries only agent-invoke.workflow.yaml). Shipped content (demos, business workflows) is seeded through the instance manifest's seed: field instead — see Instance manifest.
DEFINITION_SEED_ALWAYS proxy 1 or true re-publishes the seed directory at every boot (idempotent by content hash), so editing a YAML and restarting puts it live; 0 or false keeps the default. Development only — elsewhere it would overwrite definitions edited in the console. false

The default path is under the home directory of the process user, outside the repository, so set it explicitly (absolute path / mounted volume) in a deployment. The index only lists the runs the proxy started; the durable source of truth is Restate, so losing the index erases no runs (apps/api/src/server/run-store.ts). The SQLite driver (better-sqlite3) and the Postgres driver (pg) are imported lazily, so only the selected one is loaded. See Run store, Use a Postgres run index, and Back up and restore.

Event contracts

Read once at boot by the proxy (apps/api/src/server/event-contracts.ts).

Variable Process(es) Role Default
EVENT_CONTRACTS proxy Gate of the declared-event contracts: off, observe or enforce. observe checks every event of a declared type at ingestion, records its issues (recent events, meridian.events.contract, a warning log) and lets it through; a simulation runs and returns the issues in its report (contract). enforce refuses a non-conforming event (400, rejected) and a non-conforming simulation (400), and sources and EAI bindings filter records on their effective require. off checks nothing. An unrecognised value stops the boot. observe

The check never modifies the event, and it runs before deduplication, so a redelivered event is checked again. See Event ingestion API — Contract check.

Ports and inter-process URLs

Variable Process(es) Role Default
PORT proxy, endpoint, ui Listening port, an integer from 0 to 65535; 0 lets the system pick a free port. 4000 (proxy) / 9080 (endpoint) / 3000 (ui)
API_URL ui Proxy base URL used by ehr-lab for server-side requests. (deployment-specific)

The endpoint's PORT also feeds the default RESTATE_ENDPOINT_URL (apps/api/src/restate/server.ts, register.ts). See Configure ports and adapters.

Logging

LOG_LEVEL, LOG_PRETTY and SERVICE_NAME are read by both proxy and endpoint via apps/api/src/observability/logger.ts (pino); RESTATE_LOGGING by the endpoint's configuration (readEndpointConfig, apps/api/src/restate/config.ts).

Variable Process(es) Role Default
LOG_LEVEL proxy, endpoint Pino level: trace, debug, info, warn, error. info
LOG_PRETTY proxy, endpoint 1 forces human-readable output; 0 forces JSON. Pretty when stdout is a TTY and NODE_ENV !== "production", JSON otherwise
SERVICE_NAME proxy, endpoint Service field stamped on log lines. Also set by each entry point via initLogger. meridian
RESTATE_LOGGING endpoint Level of the Restate SDK's own console lines ([restate]…): trace, debug, info, warn or error, in any case. Read by the SDK and by the endpoint's logger transport, which writes these lines as the SDK does (apps/api/src/restate/sdk-logger.ts). Another value stops the endpoint at startup, with the SDK's own error when it loads (TypeError: unknown name <value>). info

Structured logs never contain patient payloads; a redaction list additionally censors common secret fields (*.authorization, *.apiKey, *.token, …).

Observability (OpenTelemetry)

Read by both proxy and endpoint via apps/api/src/observability/otel.ts. All OTel export is opt-in: with no OTLP endpoint set, the SDK is not started and the metric instruments are zero-cost no-ops.

Variable Process(es) Role Default
OTEL_EXPORTER_OTLP_ENDPOINT proxy, endpoint OTLP/HTTP collector endpoint (e.g. http://collector:4318). Presence enables traces + metrics + log export. (unset → export disabled)
OTEL_SERVICE_NAME proxy, endpoint Overrides the OTel service.name resource attribute. meridian-proxy / meridian-endpoint

The OTel service.version resource attribute is taken from npm_package_version (set by the package manager), defaulting to 0.0.0. When JSON logging is active and an OTLP endpoint is configured, logs are also shipped over OTLP. See Enable observability and Observability signals.

Shutdown

Read by both entry points (apps/api/src/server/api.ts, apps/api/src/restate/server.ts) and applied by apps/api/src/shutdown.ts.

Variable Process(es) Role Default
SHUTDOWN_TIMEOUT_MS proxy, endpoint Maximum time between SIGINT/SIGTERM and exit, an integer number of milliseconds from 1 to 2147483647. The proxy stops its listener, inbound reception (event sources included) and stores; the endpoint stops its agent refresh and registration, and its HTTP/2 listener closes when the process exits. Each then flushes OpenTelemetry and exits with code 0; past this delay, or when the stop fails, it exits with code 1. 10000

LLM providers

The shared low-level client (libs/llm-client/src/index.ts, @meridian/llm-client) resolves a provider's base URL and API key from the environment. It backs both the editor's workflow-generation assistant (llmCompletion) and the agent runner (plugin @posos/llm). A provider is considered configured only when its API key is present; the assistant throws if called for an unconfigured provider, and an agent falls back to a mock runner when no key is set.

Variable Process(es) Role Default
ANTHROPIC_API_KEY proxy, endpoint Anthropic API key. (none)
ANTHROPIC_BASE_URL proxy, endpoint Anthropic base URL. https://api.anthropic.com
OPENAI_API_KEY proxy, endpoint OpenAI API key. (none)
OPENAI_BASE_URL proxy, endpoint OpenAI base URL. https://api.openai.com/v1
GEMINI_API_KEY proxy, endpoint Google Gemini API key (OpenAI-compatible endpoint). Falls back to GOOGLE_API_KEY. (none)
GOOGLE_API_KEY proxy, endpoint Fallback key for the gemini provider. (none)
GEMINI_BASE_URL proxy, endpoint Gemini base URL. https://generativelanguage.googleapis.com/v1beta/openai
MISTRAL_API_KEY proxy, endpoint Mistral API key (OpenAI-compatible). (none)
MISTRAL_BASE_URL proxy, endpoint Mistral base URL. https://api.mistral.ai/v1
AZURE_OPENAI_API_KEY proxy, endpoint Azure OpenAI API key. (none)
AZURE_OPENAI_BASE_URL proxy, endpoint Azure OpenAI deployment URL (full URL of the deployment). (empty)
LOCAL_LLM_API_KEY proxy, endpoint API key for a local OpenAI-compatible server (Ollama / vLLM / LM Studio). local
LOCAL_LLM_BASE_URL proxy, endpoint Base URL of the local LLM server. http://localhost:11434/v1

Workflow assistant

Read once at proxy boot (apps/api/src/server/config.ts) for the editor's AI assistant endpoint (apps/api/src/server/routes/assist.ts).

Variable Process(es) Role Default
ASSIST_PROVIDER proxy Provider used by the workflow-generation assistant. anthropic
ASSIST_MODEL proxy Model used by the assistant. claude-sonnet-4-5
ASSIST_MAX_ATTEMPTS proxy Proposals the model may make when validation fails, each receiving the previous proposal and its errors (1 to 5). 4
ASSIST_MAX_TOKENS proxy Output token limit of each assistant call (1024 to 128000). Unset: 16384 on Anthropic, no limit sent to OpenAI-compatible providers. Keep it under the model's maximum. (unset)
ASSIST_BUDGET_MS proxy Time budget of one generation, in milliseconds (10000 to 600000). No new attempt starts with less than 20 s left, and each call's timeout is capped by the remaining budget. 150000

See Author an agent and Use the AI assistant.

Bundled-adapter fallbacks

These are read by adapters shipped with the bundled plugins only when the corresponding value is absent from the instance manifest's adapter config; config.baseUrl (etc.) in the manifest takes precedence. They apply to whichever process resolves the port (typically the endpoint for execution ports, the proxy for terminology). See Bundled plugins.

Variable Process(es) Role Default
FHIR_BASE_URL proxy, endpoint Deprecated: base URL of the FHIR R4 server used by the @posos/fhir adapters and fhir-poll when no fhir-r4 connection resolves and no config.baseUrl is set; read with a warning. Declare a connection instead (Manage connections). (none)
HERMES_BASE_URL proxy, endpoint Deprecated: base URL of the SNOMED CT terminology server (Hermes) used by the @posos/snomed adapter when no hermes connection resolves and no config.baseUrl is set; read with a warning. Declare a connection instead (Configure terminology). (none)
NOTIFY_WEBHOOK_URL proxy, endpoint Deprecated: target URL of the @posos/notify webhook adapter bound without a connection and without config.url; read with a warning. Declare a connection of type notification-webhook instead (Send notifications). (none — error if neither is set)
HERMES_LANGUAGE_REFSET_ID proxy, endpoint Deprecated: language reference set id used by the @posos/snomed adapter to pick display terms when neither the hermes connection's languageRefsetId parameter nor config.languageRefsetId is set; read with a warning. (none — Hermes default display)

See Connect a FHIR warehouse and Configure terminology.

Posos plugin (@posos/posos)

Read by the external Posos plugin client (external-plugins/posos/lib/client.ts) in whichever process loads it.

Variable Process(es) Role Default
POSOS_API_URL proxy, endpoint Deprecated: Posos edge API base URL, read with a warning when no posos-api connection resolves. Used only together with POSOS_AUDIENCE. (none)
POSOS_AUDIENCE proxy, endpoint Deprecated: OAuth client ID (IAP audience), with POSOS_API_URL. (none)
POSOS_REGION proxy, endpoint Deprecated: fr-BE selects the Belgian client, anything else fr-FR; with POSOS_API_URL. fr-FR
GOOGLE_APPLICATION_CREDENTIALS proxy, endpoint Google service-account key file, read by google-auth-library for IAP auth (also honours Application Default Credentials / Workload Identity Federation). (none)

Inbound connections

Read by the proxy at boot and applied when it checks a connection's configuration (apps/api/src/server/inbound-connections.ts).

Variable Process(es) Role Default
INBOUND_DIRECTORY_ROOT proxy Directory under which the folder of a directory-transport inbound connection must sit (previous name EAI_DIRECTORY_ROOT, still read when the new one is unset); a connection outside it is refused with Le dossier doit se trouver sous INBOUND_DIRECTORY_ROOT. (unset — any absolute path is accepted)

See Connecter un EAI.

Credentials

Read by the proxy and the endpoint at boot (apps/api/src/server/config.ts, apps/api/src/restate/config.ts, cipher in apps/api/src/server/secret-cipher.ts). The endpoint uses it to open the secrets of the store's connections, which the proxy serves sealed; set the same key ring on both.

Variable Process(es) Role Default
MERIDIAN_SECRET_KEYS proxy, endpoint Key ring that encrypts the credential store at rest: kid:base64key[,kid:base64key…], each key 32 bytes (AES-256-GCM). The first key encrypts, every key decrypts. A malformed entry stops the boot; secrets stored in cleartext or under another key are re-encrypted with the first key at boot. (unset — secrets stored in cleartext, with a warning at boot; a store that already holds encrypted secrets refuses to open)

See Manage credentials.

Maintenance scripts

Read by the scripts of apps/api/scripts/, run from a checkout of the repository with pnpm --filter @meridian/api run <script>. A relative path given to a script is read from the directory where the command is typed.

Variable Script Role Default
API_URL routing:audit Proxy base URL of the instance to audit. http://localhost:4000
DEFINITION_STORE_URL routing:audit, definitions:export, definitions:import Definition store (postgres://…). routing:audit reads the order of the active set from it in a read-only transaction; the definitions:* scripts read or write the definitions. (none — routing:audit then assumes the order of the slugs)
RUN_DB_URL routing:audit Run index (sqlite:<path> or postgres://…), read in a read-only transaction for the 7-day volume per event type. (none — volume unknown)

See Audit event routing and Manage workflow storage.

ehr-lab (mini-EHR)

Read server-side by the ehr-lab app (apps/ehr-lab/.env.example); none are exposed to the browser. In development the localhost defaults apply; on a remote host they become required and must be injected through the service environment.

Variable Process(es) Role Default
FHIR_BASE_URL ui FHIR warehouse (Blaze) — the app's sole datastore (records, directory, messaging, audit, documents). http://localhost:8090/fhir (dev)
NODE_ENV ui, proxy, endpoint Standard Node environment; affects the logger's pretty/JSON default and the ehr-lab runtime. (unset)
GOOGLE_APPLICATION_CREDENTIALS ui Google service-account key (JSON) enabling Posos autocompletion, MedicalDB (BIM) dosage suggestions, Scan&Go partner-connect auth, and Google FHIR warehouse access. (none)
POSOS_API_URL ui Posos edge API base URL (autocomplete + bim-v2 GraphQL). https://api.preprod.posos.co
POSOS_AUDIENCE ui IAP audience; gates drug autocompletion and BIM dosage suggestions (labelled demo mode when absent); shared fallback for SCANGO_AUDIENCE. (preprod)
SCANGO_BASE ui Scan&Go partner-connect base URL. https://partner-connect.preprod.posos.co
SCANGO_COMPLETE_ORIGIN ui Expected postMessage origin for Scan&Go completion. https://scan-complete.preprod.posos.co
SCANGO_AUDIENCE ui IAP audience for Scan&Go. POSOS_AUDIENCE
SCANGO_FACILITY_SLUG ui Target fhirStore in the Google warehouse. hopital-demo
SCANGO_FHIR_BASE ui Google Healthcare fhirStores base URL. (none)
SCANGO_CREDENTIALS ui Service-account key file for Scan&Go. GOOGLE_APPLICATION_CREDENTIALS

hospital-sim (simulator)

Read by the simulator (apps/hospital-sim/.env.example), which loads its own .env then the repository-root one. Everything else (arrival rates, tempo, pharmacy, bus routes, faults, the Meridian mode) is simulation configuration, edited in its dashboard and stored in its data directory.

Variable Process(es) Role Default
FHIR_BASE_URL hospital-sim FHIR warehouse shared with ehr-lab (Blaze). http://localhost:8090/fhir
EHR_LAB_URL hospital-sim ehr-lab base URL, driven by the emulated caregivers. http://localhost:3200
MERIDIAN_API_URL hospital-sim Platform proxy: EAI connection provisioning in hl7 mode, double-trigger detection. http://localhost:4000
POSOS_API_URL, POSOS_AUDIENCE, GOOGLE_APPLICATION_CREDENTIALS hospital-sim Posos BIM, used for on-demand lookups of drug codes unknown to the pharmacy formulary (the generated formulary is always loaded); without POSOS_AUDIENCE, unknown codes are looked up in the demo set. POSOS_REGION is not read by the simulator. (as for ehr-lab)
SIM_PORT hospital-sim Dashboard and control API port. 3300
SIM_BIND_HOST hospital-sim Bind address of the dashboard and control API. 127.0.0.1
SIM_MLLP_BIND_HOST hospital-sim Bind address of the internal MLLP listeners (EAI channels, DPI, PUI, SIL), independent of SIM_BIND_HOST; empty means 127.0.0.1. 127.0.0.1
SIM_DATA_DIR hospital-sim Directory of the SQLite stores. apps/hospital-sim/.data
SIM_MLLP_PORT_BASE hospital-sim First MLLP port (EAI channels base…base+3, listeners base+6…base+8). 2575
SIM_ALLOW_FHIR_PURGE hospital-sim false, 0 or no refuses a reset with FHIR purge (403); a reset without purge still works. The purge script still counts and refuses --yes unless --ignore-guard is given. Set to false on staging, where the warehouse is shared with ehr-lab and Meridian. (purge allowed)
SIM_CONTROL_TOKEN hospital-sim Bearer token required for state-changing routes from non-loopback clients. (none)
SIM_ALLOWED_HOSTS hospital-sim Comma-separated host names accepted in the Host header of state-changing requests, besides loopback names, IP literals, SIM_BIND_HOST and the machine name (cross-site and DNS-rebinding protection). (none)
SIM_TRUSTED_PEERS hospital-sim Comma-separated CIDR blocks (or addresses, IPv4/IPv6) whose state-changing requests are treated like loopback ones; Host, Origin and JSON checks still apply. Set by docker-compose.yml to the Docker private ranges so the host's dashboard can write (the port is published on 127.0.0.1 only). An unreadable value stops the launch. (none); compose: 172.16.0.0/12,192.168.0.0/16,10.0.0.0/8

The ehr-lab app does not talk to the proxy directly: clinical validations pass through the FHIR warehouse (a Task), and the two apps share only Blaze (FHIR_BASE_URL). See Connect a FHIR warehouse, Scan&Go / FHIR mapping (apps/ehr-lab/docs/resources/reference/fhir-mapping.md), and Set the instance locale.

97 documents19 sectionssource : /docs · généré au build