Server Configuration
The FHIR server loads its configuration with Figment, merging two sources:
let config: ServerConfig = Figment::new()
.merge(Toml::file("haste.toml"))
.merge(Env::prefixed("HASTE_"))
.extract()?;
haste.toml— read relative to the process's working directory (backend/, when runningcargo run server startfrom there). If the file doesn't exist, this step is silently skipped — there's no error, the server just falls through to defaults/env.HASTE_-prefixed environment variables — merged after the TOML file, so environment variables win on any key set in both.
Anything left unset by either source falls back to the default listed below.
Environment Variable Naming
- Top-level fields:
HASTE_<FIELD>— e.g.HASTE_API_URI,HASTE_CERTIFICATION_DIR,HASTE_MAX_REQUEST_BODY_SIZE. - Nested sections:
HASTE_<SECTION>.<field>— the prefix plus the section name, then a literal dot, then the field name exactly as it appears in TOML (lowercase) — e.g.HASTE_REPO.database_url,HASTE_SEARCH.username,HASTE_SECURITY.publicize_fhir_metadata. - Sections that select a backend/provider via a tag (
repo,search,email,security.encryption) need that tag set too, e.g.HASTE_REPO.backend=postgres.
Reference
Top level
| Key | Env var | Default | Notes |
|---|---|---|---|
api_uri | HASTE_API_URI | http://localhost:3000 | Root URL the FHIR server is hosted at; used to build absolute URLs (OIDC discovery, FHIR base URLs, etc). |
certification_dir | HASTE_CERTIFICATION_DIR | certifications | Directory used for JWT signing/verification key material. |
admin_app_redirect_uri | HASTE_ADMIN_APP_REDIRECT_URI | http://*.localhost:3001 | Redirect target for the hardcoded admin app OIDC client. |
allow_artifact_mutations | HASTE_ALLOW_ARTIFACT_MUTATIONS | false | Allows mutating built-in/embedded artifact resources. |
max_request_body_size | HASTE_MAX_REQUEST_BODY_SIZE | 4194304 (4MB) | Max accepted request body size, in bytes. |
port | HASTE_PORT | 3000 | Port the server listens on. |
[fhir]
| Key | Env var | Default |
|---|---|---|
fhir.delete_limit | HASTE_FHIR.delete_limit | 100 — max resources removed by a single type/system-level delete. |
[repo] — resource storage backend
Tagged by backend; only postgres exists today.
| Key | Env var | Default |
|---|---|---|
repo.backend | HASTE_REPO.backend | postgres |
repo.database_url | HASTE_REPO.database_url | postgresql://postgres:postgres@127.0.0.1/haste_health |
repo.max_connections | HASTE_REPO.max_connections | 20 |
[search] — search index backend
Tagged by backend; two backends exist: elasticsearch (the default) and postgres (search indexed in the same Postgres server as [repo], no Elasticsearch cluster required).
backend = "elasticsearch" (default):
| Key | Env var | Default |
|---|---|---|
search.url | HASTE_SEARCH.url | http://localhost:9200 |
search.username | HASTE_SEARCH.username | elastic |
search.password | HASTE_SEARCH.password | elastic |
search.prune_removed_search_parameters | HASTE_SEARCH.prune_removed_search_parameters | false — lets migrate search drop the mapping (and indexed data) for a removed search parameter. Off by default: removed parameters are logged, never deleted, since Elasticsearch mappings are append-only and dropping one needs a reindex. |
backend = "postgres":
| Key | Env var | Default |
|---|---|---|
search.database_url | HASTE_SEARCH.database_url | postgresql://postgres:postgres@127.0.0.1/haste_health_search — a separate database from [repo]'s |
search.max_connections | HASTE_SEARCH.max_connections | 20 |
[email] — optional
None/absent by default (email sending disabled). Tagged by backend; only sendgrid exists today.
| Key | Env var |
|---|---|
email.backend | HASTE_EMAIL.backend (sendgrid) |
email.api_key | HASTE_EMAIL.api_key |
email.from_address | HASTE_EMAIL.from_address |
[rate_limits]
| Key | Env var | Default |
|---|---|---|
rate_limits.rate_limit_subscription_tiers | HASTE_RATE_LIMITS.rate_limit_subscription_tiers | unset — a 4-element array, one limit per subscription tier (Free/Professional/Team/Unlimited). |
rate_limits.rate_limit_window_seconds | HASTE_RATE_LIMITS.rate_limit_window_seconds | 86400 (1 day) |
rate_limits.rate_limit_operation_points | HASTE_RATE_LIMITS.rate_limit_operation_points | 100 |
[monitoring]
| Key | Env var | Default |
|---|---|---|
monitoring.audit_enabled | HASTE_MONITORING.audit_enabled | false — gates the auditing middleware. |
monitoring.ip_source | HASTE_MONITORING.ip_source | connect_info — one of connect_info, cf_connecting_ip, x_real_ip; how client IP is derived. |
[security]
| Key | Env var | Default |
|---|---|---|
security.publicize_fhir_metadata | HASTE_SECURITY.publicize_fhir_metadata | true — exposes /fhir/{version}/metadata without authentication. Set to false to require auth on the capability statement. |
security.aes_key | HASTE_SECURITY.aes_key | unset |
security.certification_key | HASTE_SECURITY.certification_key | unset |
[security.mfa]
| Key | Env var | Default |
|---|---|---|
security.mfa.max_credentials_per_user | HASTE_SECURITY.mfa.max_credentials_per_user | 1 |
[security.encryption] — secret provider for aes_key / certification_key
Tagged by type: environment (default), gcp, or aws.
| Key | Env var | Default |
|---|---|---|
security.encryption.type | HASTE_SECURITY.encryption.type | environment |
security.encryption.prefix (environment only) | HASTE_SECURITY.encryption.prefix | HASTE_SECRET_ |
security.encryption.project_id (gcp only) | HASTE_SECURITY.encryption.project_id | — |
security.encryption.region (aws only) | HASTE_SECURITY.encryption.region | — |
[operations]
| Key | Env var | Default | Notes |
|---|---|---|---|
operations.deno_pool_threads | HASTE_OPERATIONS.deno_pool_threads | Roughly half the host's available parallelism (minimum 2) | Dedicated OS threads executing custom (tenant-authored) FHIR operation scripts in isolated Deno/V8 sandboxes. Each thread runs one script at a time — this is a hard ceiling on concurrent custom-operation invocations. |
Example haste.toml
api_uri = "http://localhost:3000"
certification_dir = "certifications"
admin_app_redirect_uri = "http://*.localhost:3001"
max_request_body_size = 20971520 # 20MB
[repo]
backend = "postgres"
database_url = "postgresql://postgres:postgres@localhost:5432/haste_health"
max_connections = 20
[search]
backend = "elasticsearch"
url = "http://localhost:9200"
username = "elastic"
password = "elastic"
# Alternative to the [search] block above -- no Elasticsearch cluster needed:
# [search]
# backend = "postgres"
# database_url = "postgresql://postgres:postgres@127.0.0.1/haste_health_search"
# max_connections = 20
[operations]
deno_pool_threads = 4
[monitoring]
audit_enabled = false
[security]
publicize_fhir_metadata = true
aes_key = "AES_KEY"
certification_key = "CERTIFICATE_KEY"
[security.mfa]
max_credentials_per_user = 1
[security.encryption]
type = "environment"
prefix = "HASTE_SECRET_"
Example Environment Variables (Container Deployment)
HASTE_API_URI=http://localhost:3000
HASTE_CERTIFICATION_DIR=certifications
HASTE_ADMIN_APP_REDIRECT_URI=http://*.localhost:3001
HASTE_REPO.backend=postgres
HASTE_REPO.database_url=postgresql://postgres:postgres@postgres:5432/haste_health
HASTE_REPO.max_connections=20
HASTE_SEARCH.backend=elasticsearch
HASTE_SEARCH.url=http://elasticsearch:9200
HASTE_SEARCH.username=elastic
HASTE_SEARCH.password=elastic