TEDY.18 — Configuration & environment variables
State: Built — env surface implemented. Prefixes VITE_/TX_/TEDY_/OAUTH_ are allow-listed in vite.config.ts; all vars are read + defaulted in src/config/tx.ts. Wired features consume them now; not-yet-ported services read the var but carry a TODO(TEDY.18) until the feature lands.
Template: tool-feature. Prefix: TEDY.
Why
The reference editor’s web app is configured entirely through a runtime web.env (Docker env), and its API through a server.env. TEDY, by contrast, is a federated module hosted inside the EMR shell, so most of the reference’s frontend configuration is either (a) already covered by a small set of VITE_* build/runtime vars, (b) owned by the host shell (auth, UI language, theming, branding), or (c) out of scope because the corresponding feature (FML editor, PlantUML, FSH/SUSHI “chef”, FHIR→UML, Swagger UI) is not ported.
This spec is the single place that:
- lists the full reference frontend config surface and what each var does;
- maps each var to its TEDY equivalent, or records why it is host-owned / out-of-scope;
- defines the recommended TEDY env list and neutral
.env.exampleentries.
Source of truth: the reference project’s public installation guide plus a live reference dev deployment’s web.env / server.env. Per repository policy, no real hostnames, secrets, or brand strings are reproduced here — example values use neutral *.example placeholders, real values live only in the gitignored local .env, and reference env-var names are shown with their brand prefix normalized to TX_ (e.g. the reference’s server-API var appears as TX_API).
Why TEDY vars carry a VITE_ prefix
Vite only exposes env vars prefixed with VITE_ to client code through import.meta.env; every other var in the environment is deliberately withheld from the browser bundle so co-located server secrets (DB passwords, API keys) can’t leak into shipped JS. Any value the frontend must read therefore has to start with VITE_ (or a configured envPrefix). The reference web.env vars need no such prefix because they are not read by Vite — the reference container injects them into a generated runtime config at startup, whereas TEDY reads its config at build/serve time through Vite.
1. Reference frontend config (web.env) — full surface
| Var | Purpose | Example value | TEDY disposition |
|---|---|---|---|
BASE_HREF | App mount path for relative URLs | / or /tedy | Build-time — host mounts TEDY under its route |
TX_API | Terminology-server API base | /api | ✅ Ported → VITE_TX_TARGET |
SWAGGER_URL | Swagger UI path | /swagger/ | ❌ Out of scope (no Swagger UI in TEDY) |
DEFAULT_LANGUAGE | Fallback UI language | en | ⚠️ Host-owned (@helex/i18n) |
UI_LANGUAGES | Selectable UI languages (JSON array) | ["en","et","lt","cs"] | ⚠️ Host-owned |
CONTENT_LANGUAGES | Multilingual content input languages (JSON array) | ["en","et","lt","ru","de","fr"] | 🟡 Candidate — drives TEDY’s localized-name editors |
EXTRA_LANGUAGES | Display-name overrides for extra content langs (JSON object) | {"cs":{"en":"Czech"}} | 🟡 Candidate (only with extra content langs) |
OAUTH_ISSUER | OIDC realm / issuer URL | dummy or realm URL | ⚠️ Host-owned auth |
OAUTH_CLIENT_ID | OAuth2 client id | dummy | ⚠️ Host-owned auth |
OAUTH_SCOPE | OAuth2 scopes at login | openid | ⚠️ Host-owned auth |
GUEST_DISABLED | Disable anonymous access | false | ⚠️ Host-owned auth |
SNOWSTORM_URL | SNOMED (Snowstorm) API base | https://snowstorm.example/ | 🟡 Candidate (TEDY reads SNOMED via backend /snomed; direct base only if deep-calling) |
SNOMED_BROWSER_URL | SNOMED browser deep-link base | https://snomed.example/ | 🟡 Candidate — TEDY has SNOMED search/global-search |
SNOMED_BROWSER_DAILY_BUILD_URL | SNOMED daily-build browser base | https://snomed.example/ | 🟡 Candidate (optional variant) |
FML_EDITOR | FHIR Mapping-Language editor service | /fml-editor | ❌ Out of scope (not ported) |
PLANT_UML_URL | PlantUML diagram service | /plantuml | ❌ Out of scope |
CHEF_URL | FSH/SUSHI/GoFSH compiler service | /chef | ❌ Out of scope (IG authoring) |
CHEF_FHIR_VERSION | FHIR version for the compiler service | 4.3.0 | ❌ Out of scope |
FHIR_UML_CONVERTER_API | FHIR→UML converter service | https://svc.example/fhir2uml/api | ❌ Out of scope |
SKIN / SKIN_URL | Named theme / external stylesheet | (named theme) | ⚠️ Host-owned theming |
BRANDING | Product name in header | (product name) | ⚠️ Host-owned (header is the EMR shell) |
Legend: ✅ ported · 🟡 candidate to add · ⚠️ host-owned (inherited) · ❌ out of scope (feature not ported).
2. TEDY’s implemented env surface
All client-exposed vars are read once in modules/tedy/frontend/src/config/tx.ts. The proxy target and base are read Node-side in vite.config.ts. Prefixes: VITE_ (kept for backward compatibility + language vars), TX_ (terminology-server side integrations), TEDY_ (the app itself), OAUTH_ (auth).
App (TEDY_ prefix)
| Var | Purpose | Default | Consumed | Legacy fallback |
|---|---|---|---|---|
TEDY_TARGET | Terminology-server API base (incl. api segment; differs per deployment, e.g. /api vs /lmb-api). Proxied /txapi → {target}. | https://tx.example/api | ✅ vite proxy | VITE_TX_TARGET |
TEDY_BASE_HREF | App mount path (Vite base). | /tedy/ | ✅ vite base | — |
TEDY_BRANDING | Product name in TEDY-owned chrome. | TEDY | ✅ document title (standalone) | — |
TEDY_MSDEVOPS_ENABLED | Offer the Azure DevOps git integration on a Space’s Metadata + Repository tabs. Unset/0 hides it. | 0 | ✅ MSDEVOPS_ENABLED | VITE_TX_MSDEVOPS_ENABLED |
Terminology-server integrations (TX_ prefix)
| Var | Purpose | Consumed | Legacy fallback |
|---|---|---|---|
TX_PUBLISHER_VS_URL | Publisher ValueSet canonical — resolves publisher display names. | ✅ | VITE_TX_PUBLISHER_VS_URL |
TX_PUBLISHER_CS_URL | Publisher CodeSystem canonical. | ✅ | VITE_TX_PUBLISHER_CS_URL |
TX_VIEWER_TARGET | Terminology eXplorer base — opens a resource’s web page + FHIR API from Servers → Resources. | ✅ | VITE_TX_VIEWER_URL |
TX_SNOWSTORM_TARGET | Snowstorm API base (direct calls; TEDY normally reads SNOMED via backend /snomed). | 🟡 exported; TODO | — |
TX_SNOWSTORM_BROWSER | SNOMED browser deep-link base. | ✅ Global Search SNOMED group | — |
TX_SNOWSTORM_DAILY_BROWSER | SNOMED daily-build browser base. | 🟡 exported; TODO | — |
TX_CHEF_TARGET / TX_CHEF_FHIR_VERSION | FSH/SUSHI compiler (not ported). | ⏸ TODO | — |
TX_PLANTUML_TARGET | PlantUML service (not ported). | ⏸ TODO | — |
TX_FHIR2UML_TARGET | FHIR→UML converter (not ported). | ⏸ TODO | — |
TX_SWAGGER_TARGET | Swagger UI (not ported). | ⏸ TODO | — |
Content / UI languages (VITE_ prefix, JSON-encoded)
| Var | Purpose | Default |
|---|---|---|
VITE_DEFAULT_LANGUAGE | Fallback content language. | en |
VITE_UI_LANGUAGES | Selectable UI languages. | ["en"] |
VITE_CONTENT_LANGUAGES | Languages offered in localized-name editors. | ["en"] |
VITE_EXTRA_LANGUAGES | Display-name overrides for extra content languages. | {} |
✅ VITE_CONTENT_LANGUAGES (+ VITE_EXTRA_LANGUAGES display names) feed every multilingual editor via components/TxLocalizedName (a thin AppLocalizedName wrapper injecting config/tx.ts’s CONTENT_LANGUAGE_OPTIONS). When the var is unset the editors keep their built-in default, so this is opt-in.
Auth (OAUTH_ prefix — host-owned in a deployment)
OAUTH_ISSUER, OAUTH_CLIENT_ID (exported for parity/dev), OAUTH_GUEST_DISABLED (false). ⚠️ Because OAUTH_ is allow-listed for the client bundle, never place OAUTH_CLIENT_SECRET (or any secret) in the frontend build environment — it would be exposed.
Legend: ✅ wired now · 🟡 read+exported, consumer wiring is a TODO(TEDY.18) · ⏸ feature not ported (config reserved).
3. Why the prefixes (and why not drop VITE_)
Vite exposes to the client only env vars matching envPrefix; everything else is withheld so co-located secrets can’t leak. We allow-list ['VITE_','TX_','TEDY_','OAUTH_']:
VITE_is retained so pre-existing local envs and the language vars keep working — the rename is non-breaking.TX_/TEDY_/OAUTH_are TEDY’s own, human-readable prefixes.- An empty prefix (
'') is forbidden — it would expose every var. Vite’s built-ins (import.meta.env.MODE/DEV/PROD/BASE_URL) are always present regardless of prefix.
4. Migration
Legacy names are still honored (via the VITE_ fallbacks in config/tx.ts and the Node-side proxy fallback), so nothing breaks on upgrade. To move an env file to the current names, use the copy-migrator (never mutates the source):
scripts/migrate-tedy-env.sh path/to/old.env # → old.env.migrated
scripts/migrate-tedy-env.sh path/to/old.env new.env # → new.env
scripts/migrate-tedy-env.sh path/to/old.env - # → stdout
Old → new mapping applied by the script:
| Legacy / reference name | Current name |
|---|---|
VITE_TX_TARGET | TEDY_TARGET |
BASE_HREF | TEDY_BASE_HREF |
BRANDING | TEDY_BRANDING |
VITE_TX_MSDEVOPS_ENABLED | TEDY_MSDEVOPS_ENABLED |
DEFAULT_LANGUAGE | VITE_DEFAULT_LANGUAGE |
UI_LANGUAGES | VITE_UI_LANGUAGES |
CONTENT_LANGUAGES | VITE_CONTENT_LANGUAGES |
EXTRA_LANGUAGES | VITE_EXTRA_LANGUAGES |
VITE_TX_PUBLISHER_VS_URL | TX_PUBLISHER_VS_URL |
VITE_TX_PUBLISHER_CS_URL | TX_PUBLISHER_CS_URL |
VITE_TX_VIEWER_URL | TX_VIEWER_TARGET |
SNOWSTORM_URL | TX_SNOWSTORM_TARGET |
SNOMED_BROWSER_URL | TX_SNOWSTORM_BROWSER |
SNOMED_BROWSER_DAILY_BUILD_URL | TX_SNOWSTORM_DAILY_BROWSER |
CHEF_URL | TX_CHEF_TARGET |
CHEF_FHIR_VERSION | TX_CHEF_FHIR_VERSION |
PLANT_UML_URL | TX_PLANTUML_TARGET |
FHIR_UML_CONVERTER_API | TX_FHIR2UML_TARGET |
SWAGGER_URL | TX_SWAGGER_TARGET |
GUEST_DISABLED | OAUTH_GUEST_DISABLED |
The full neutral template lives in modules/tedy/frontend/.env.example; real values live only in the gitignored local .env.
Host-owned / out-of-scope (not TEDY vars)
Theming (SKIN, SKIN_URL) and UI shell are the EMR host’s; OAUTH_SCOPE and the SSO realm/client are host auth. FML_EDITOR has no TEDY consumer and is intentionally omitted.
5. Backend config (context only — not owned by TEDY)
The reference API (server.env) is a separate deployment concern; TEDY only talks to it through VITE_TX_TARGET. For orientation, its surface covers: PostgreSQL connection + pooling (DB_URL, DB_*_PASSWORD, DB_POOL_SIZE), CORS/JVM options, web/API self-URLs (TX_WEB_URL, TX_API_URL), OAuth JWKS validation, Snowstorm base + branch (SNOWSTORM_URL, SNOWSTORM_BRANCH), object storage (MinIO BOB_MINIO_*), the compiler service URL, a GitHub App (id/name/client id/secret) for content publishing, SMTP for notifications, and a conformance-validator target. Secrets (DB/object-store passwords, GitHub client secret, SMTP credentials) and real hostnames are intentionally not reproduced. These are provisioned per deployment by whoever operates the terminology server; nothing here is a TEDY frontend build input.
Open items (TODOs the vars are reserved for)
Wired: SNOMED browser deep-link (Global Search), content languages (all editors via TxLocalizedName), branding (document title). Remaining:
- SNOMED daily-build browser (
TX_SNOWSTORM_DAILY_BROWSER) and direct Snowstorm (TX_SNOWSTORM_TARGET) — no consumer yet (TEDY reads SNOMED via the backend/snomed). - Not-ported services (
TX_CHEF_*,TX_PLANTUML_TARGET,TX_FHIR2UML_TARGET,TX_SWAGGER_TARGET) — consume when/if those features are ported.