-
Notifications
You must be signed in to change notification settings - Fork 630
Expand file tree
/
Copy path.env.example
More file actions
3484 lines (2868 loc) · 143 KB
/
.env.example
File metadata and controls
3484 lines (2868 loc) · 143 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
# =============================================================================
# ContextForge Configuration Example
# =============================================================================
# NOTE ON SOURCES
# Values below match Pydantic Settings defaults unless noted.
# Most env vars below are Pydantic Settings (mcpgateway/config.py).
# Some are runtime/launcher envs (shell scripts, entrypoint) or direct env reads
# in modules (not part of Settings). Those sections are labeled explicitly.
# =============================================================================
# Required: change before use
# =============================================================================
# Admin UI HTTP Basic Auth credentials
# PRODUCTION: Change these values
BASIC_AUTH_USER=admin
BASIC_AUTH_PASSWORD=changeme
# JWT secret used to sign tokens
# PRODUCTION: Use a strong, unique value
JWT_SECRET_KEY=my-test-key-but-now-longer-than-32-bytes
# Passphrase used to encrypt stored auth secrets
# PRODUCTION: Use a strong, unique value
AUTH_ENCRYPTION_SECRET=my-test-salt
# Bootstrap admin credentials (email auth)
# PRODUCTION: Change these values
PLATFORM_ADMIN_EMAIL=admin@example.com
PLATFORM_ADMIN_PASSWORD=changeme
DEFAULT_USER_PASSWORD=changeme
# =============================================================================
# Security Defaults (secure by default)
# =============================================================================
# These settings are enabled by default for security. Only disable for backward
# compatibility with legacy tokens that lack these claims.
# Require JTI (JWT ID) claim in all tokens for revocation support
# Tokens without JTI cannot be revoked before expiration
REQUIRE_JTI=true
# Require expiration (exp) claim in all tokens
# Tokens without expiration never expire and pose a security risk
REQUIRE_TOKEN_EXPIRATION=true
# Disable public user self-registration (admin must create accounts)
PUBLIC_REGISTRATION_ENABLED=false
# Allow entities to have public visibility (set false to block in team scope)
ALLOW_PUBLIC_VISIBILITY=true
# Basic Auth is DISABLED by default for security - use JWT tokens instead
# Only enable for backwards compatibility with legacy clients
# API_ALLOW_BASIC_AUTH=false
# DOCS_ALLOW_BASIC_AUTH=false
# -----------------------------------------------------------------------------
# SSRF Protection (Server-Side Request Forgery)
# -----------------------------------------------------------------------------
# Prevents the gateway from being used to access internal resources or cloud
# metadata services. Enabled by default with safe settings for dev/internal use.
# Master switch for SSRF protection (default: true)
# SSRF_PROTECTION_ENABLED=true
# Allow localhost/loopback addresses (127.0.0.0/8, ::1)
# Default: false (strict)
# SSRF_ALLOW_LOCALHOST=false
# Allow RFC 1918 private network addresses (10.x, 172.16-31.x, 192.168.x)
# Default: false (strict). Use SSRF_ALLOWED_NETWORKS for explicit exceptions.
# SSRF_ALLOW_PRIVATE_NETWORKS=false
# Optional CIDR allowlist when SSRF_ALLOW_PRIVATE_NETWORKS=false
# Only private IPs in these ranges are allowed.
# SSRF_ALLOWED_NETWORKS=["10.20.0.0/16","192.168.50.0/24"]
# Fail closed on DNS resolution errors (default: true)
# URLs that cannot be resolved are rejected
# SSRF_DNS_FAIL_CLOSED=true
# Networks to block (JSON array of CIDR ranges) - ALWAYS blocked regardless of above
# Default blocks cloud metadata endpoints. Add more for stricter security.
# SSRF_BLOCKED_NETWORKS=["169.254.169.254/32","169.254.169.123/32","fd00::1/128","169.254.0.0/16","fe80::/10"]
# Hostnames to block (JSON array) - case-insensitive matching
# SSRF_BLOCKED_HOSTS=["metadata.google.internal","metadata.internal"]
# Example: STRICT mode (external endpoints only, no internal access)
# SSRF_PROTECTION_ENABLED=true
# SSRF_ALLOW_LOCALHOST=false
# SSRF_ALLOW_PRIVATE_NETWORKS=false
# SSRF_ALLOWED_NETWORKS=[]
# SSRF_BLOCKED_NETWORKS=["169.254.169.254/32","169.254.169.123/32","fd00::1/128","169.254.0.0/16","fe80::/10","100.64.0.0/10"]
# The 100.64.0.0/10 range is Carrier-Grade NAT (CGNAT) which some cloud providers use
# -----------------------------------------------------------------------------
# Content Security - Size Limits and MIME Type Restrictions (US-2)
# -----------------------------------------------------------------------------
# Maximum content sizes (in bytes) to prevent DoS attacks via large uploads
# Maximum size for resource content (default: 102400 = 100KB)
# Resources exceeding this limit will be rejected with 413 Payload Too Large
# CONTENT_MAX_RESOURCE_SIZE=102400
# Maximum size for prompt templates (default: 10240 = 10KB)
# Prompts exceeding this limit will be rejected with 413 Payload Too Large
# CONTENT_MAX_PROMPT_SIZE=10240
# Allowed MIME types for resources (JSON array or comma-separated list)
# In strict mode, only MIME types explicitly listed here are accepted.
# Vendor types (application/x-*, text/x-*) and suffix types (+json, +xml) must be
# explicitly added to this list if needed - they are NOT automatically allowed.
# Default: text/plain,text/markdown,text/html,text/csv,application/json,application/xml,application/pdf,...
# Both formats are accepted:
# CONTENT_ALLOWED_RESOURCE_MIMETYPES=["text/plain","text/markdown","application/json"]
# CONTENT_ALLOWED_RESOURCE_MIMETYPES=text/plain,text/markdown,application/json
# Enable strict MIME type validation for resources (default: false)
# Set to true to reject disallowed MIME types; false logs violations without blocking
# CONTENT_STRICT_MIME_VALIDATION=false
# =============================================================================
# Project defaults (batteries-included overrides)
# =============================================================================
# These values intentionally differ from config.py defaults to provide a working
# local/dev setup out of the box. Comment out anything you do not want to override.
# Bind to all interfaces for local containers and remote access
HOST=0.0.0.0
# Local origin used for CORS/cookies in development examples
APP_DOMAIN=http://localhost
# Enable Admin UI and Admin API for local development
MCPGATEWAY_UI_ENABLED=true
MCPGATEWAY_ADMIN_API_ENABLED=true
# Local/dev SSRF compatibility: allow local services and fail open on DNS lookups.
# Keep strict values (false/false/true) in production environments.
SSRF_ALLOW_LOCALHOST=true
SSRF_ALLOW_PRIVATE_NETWORKS=true
SSRF_DNS_FAIL_CLOSED=false
# Disable WebSocket relay and reverse-proxy transports by default.
# Enable only when those transports are explicitly required.
MCPGATEWAY_WS_RELAY_ENABLED=false
MCPGATEWAY_REVERSE_PROXY_ENABLED=false
# Relax cookie security for local HTTP development
SECURE_COOKIES=false
# Enable validation middleware and experimental IO validation for visibility
EXPERIMENTAL_VALIDATE_IO=true
VALIDATION_MIDDLEWARE_ENABLED=true
# Permission audit logging (RBAC checks) - disabled by default for performance
PERMISSION_AUDIT_ENABLED=false
# Add SQL injection pattern on top of the default dangerous patterns
DANGEROUS_PATTERNS=["[;&|`$(){}\\[\\]<>]", "\\.\\.[\\\\/]", "[\\x00-\\x1f\\x7f-\\x9f]", "(?i)(drop|delete|insert|update|select)\\s+(table|from|into|where)"]
# Loosen password complexity for local bootstrap (production should re-enable)
PASSWORD_REQUIRE_UPPERCASE=false
PASSWORD_REQUIRE_LOWERCASE=false
PASSWORD_REQUIRE_SPECIAL=false
# Longer UI tool test timeout for slower dev environments
MCPGATEWAY_UI_TOOL_TEST_TIMEOUT=120000
# Slow down health polling and extend config cache for local dev
HEALTH_CHECK_INTERVAL=300
GLOBAL_CONFIG_CACHE_TTL=300
# Log to file by default for local debugging
LOG_FILE=mcpgateway.log
LOG_FOLDER=logs
# Local OTEL collector endpoint
OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4317
# =============================================================================
# Hot toggles (commented quick switches)
# =============================================================================
# These are frequently changed flags. If a key is already set in the
# Project defaults block above, change it there instead of uncommenting here.
# Feature flags / UX
# MCPGATEWAY_UI_ENABLED=false
# MCPGATEWAY_ADMIN_API_ENABLED=false
# PLUGINS_ENABLED=false
# MCPGATEWAY_CATALOG_ENABLED=true
# LLMCHAT_ENABLED=true
# MCPGATEWAY_STDIO_TRANSPORT_ENABLED=false
# PLUGINS_CAN_OVERRIDE_RBAC=false
# PLUGINS_CAN_OVERRIDE_AUTH_HEADERS=false
# Direct proxy mode
# MCPGATEWAY_DIRECT_PROXY_ENABLED=false
# MCPGATEWAY_DIRECT_PROXY_TIMEOUT=30
# Observability / metrics
# OBSERVABILITY_ENABLED=false
# OTEL_ENABLE_OBSERVABILITY=false
# ENABLE_METRICS=true
# DB_METRICS_RECORDING_ENABLED=true
# METRICS_AGGREGATION_AUTO_START=false
# Logging / audit
# STRUCTURED_LOGGING_DATABASE_ENABLED=false
# AUDIT_TRAIL_ENABLED=false
# PERMISSION_AUDIT_ENABLED=false
# SECURITY_LOGGING_ENABLED=false
# TOKEN_USAGE_LOGGING_ENABLED=true
# TOKEN_LAST_USED_UPDATE_INTERVAL_MINUTES=5
# Security / auth (see also "Security Defaults" section above)
# AUTH_REQUIRED=true
# MCP_CLIENT_AUTH_ENABLED=true
# TRUST_PROXY_AUTH=false
# TRUST_PROXY_AUTH_DANGEROUSLY=false # DANGER: Only set true behind a strictly trusted auth proxy
# ALLOW_UNAUTHENTICATED_ADMIN=false # DANGER: Only for local dev — grants admin to unauthenticated requests
# SECURITY_HEADERS_ENABLED=true
# CORS_ALLOW_CREDENTIALS=true
# SECURE_COOKIES=true
# REQUIRE_USER_IN_DB=false
# Performance / reliability
# COMPRESSION_ENABLED=true
# VALIDATION_MIDDLEWARE_ENABLED=false
# CORRELATION_ID_ENABLED=false
# TEMPLATES_AUTO_RELOAD=false
# MCP_SESSION_POOL_ENABLED=false
# ANYIO_CANCEL_DELIVERY_PATCH_ENABLED=false
# Rust MCP (simple)
# RUST_MCP_BUILD=false # build the Rust MCP runtime into Containerfile.lite images
# RUST_MCP_MODE=off # off | shadow | edge | full
# RUST_MCP_LOG=warn # default Rust sidecar log filter for the simple mode flow
#
# RUST_MCP_MODE=shadow -> Rust sidecar enabled, but public /mcp stays on Python for safe fallback
# RUST_MCP_MODE=edge -> direct public /mcp on Rust with managed UDS sidecar defaults
# RUST_MCP_MODE=full -> edge + Rust session/event-store/resume/live-stream/affinity cores
#
# Advanced Rust MCP overrides
# RUST_MCP_SESSION_AUTH_REUSE=false # advanced override for the fast direct public Rust session-auth path; prefer RUST_MCP_MODE presets above
# EXPERIMENTAL_RUST_MCP_RUNTIME_ENABLED=
# EXPERIMENTAL_RUST_MCP_RUNTIME_URL=http://127.0.0.1:8787
# EXPERIMENTAL_RUST_MCP_RUNTIME_UDS=/tmp/contextforge-mcp-rust.sock
# EXPERIMENTAL_RUST_MCP_RUNTIME_TIMEOUT_SECONDS=30
# EXPERIMENTAL_RUST_MCP_SESSION_CORE_ENABLED= # enable Rust-owned MCP session metadata/lifecycle increment
# EXPERIMENTAL_RUST_MCP_EVENT_STORE_ENABLED= # enable Rust-owned resumable event-store backend
# EXPERIMENTAL_RUST_MCP_RESUME_CORE_ENABLED= # enable Rust-owned public GET /mcp replay/resume path
# EXPERIMENTAL_RUST_MCP_LIVE_STREAM_CORE_ENABLED= # enable Rust-owned public GET /mcp live SSE path
# EXPERIMENTAL_RUST_MCP_AFFINITY_CORE_ENABLED= # enable Rust-owned session-affinity forwarding path
# EXPERIMENTAL_RUST_MCP_SESSION_AUTH_REUSE_ENABLED= # enable Rust-owned session-bound auth-context reuse
# EXPERIMENTAL_RUST_MCP_RUNTIME_MANAGED= # launcher env, not a Pydantic setting
# ENABLE_RUST_MCP_RMCP_BUILD= # container build arg override for rmcp-enabled Rust MCP binary
# MCP_RUST_USE_RMCP_UPSTREAM_CLIENT= # runtime override for official rust-sdk upstream tools/call client
# MCP_RUST_LISTEN_HTTP=127.0.0.1:8787 # runtime env for bundled Rust sidecar
# MCP_RUST_LISTEN_UDS=/tmp/contextforge-mcp-rust.sock
# MCP_RUST_SESSION_CORE_ENABLED= # explicit sidecar env; defaults from EXPERIMENTAL_RUST_MCP_SESSION_CORE_ENABLED
# MCP_RUST_SESSION_TTL_SECONDS=3600
# MCP_RUST_EVENT_STORE_ENABLED= # explicit sidecar env; defaults from EXPERIMENTAL_RUST_MCP_EVENT_STORE_ENABLED
# MCP_RUST_RESUME_CORE_ENABLED= # explicit sidecar env; defaults from EXPERIMENTAL_RUST_MCP_RESUME_CORE_ENABLED
# MCP_RUST_LIVE_STREAM_CORE_ENABLED= # explicit sidecar env; defaults from EXPERIMENTAL_RUST_MCP_LIVE_STREAM_CORE_ENABLED
# MCP_RUST_AFFINITY_CORE_ENABLED= # explicit sidecar env; defaults from EXPERIMENTAL_RUST_MCP_AFFINITY_CORE_ENABLED
# MCP_RUST_SESSION_AUTH_REUSE_ENABLED= # explicit sidecar env; defaults from EXPERIMENTAL_RUST_MCP_SESSION_AUTH_REUSE_ENABLED
# MCP_RUST_SESSION_AUTH_REUSE_TTL_SECONDS=30
# MCP_RUST_EVENT_STORE_MAX_EVENTS_PER_STREAM=100
# MCP_RUST_EVENT_STORE_TTL_SECONDS=3600
# MCP_RUST_EVENT_STORE_POLL_INTERVAL_MS=250
# MCP_RUST_LOG= # advanced runtime log override for the bundled Rust sidecar
# MCP_RUST_BACKEND_RPC_URL=http://127.0.0.1:4444/_internal/mcp/rpc
# MCP_RUST_REDIS_URL=redis://redis:6379/0
# MCP_RUST_CACHE_PREFIX=mcpgw:
# MCP_RUST_DATABASE_URL=postgresql://postgres:mysecretpassword@pgbouncer:6432/mcp
# MCP_RUST_DB_POOL_MAX_SIZE=20
# =============================================================================
# Performance Tuning (quick reference)
# =============================================================================
# Use this section to tune throughput, latency, and cache behavior.
# If a key is already set in the Project defaults block, change it there instead.
# Detailed explanations for each setting appear later in this file.
# -----------------------------------------------------------------------------
# Cache TTLs (seconds)
# -----------------------------------------------------------------------------
# AUTH / registry / admin caches
# AUTH_CACHE_USER_TTL=60
# AUTH_CACHE_REVOCATION_TTL=30
# AUTH_CACHE_TEAM_TTL=60
# AUTH_CACHE_ROLE_TTL=60
# AUTH_CACHE_TEAMS_TTL=60
# REGISTRY_CACHE_TOOLS_TTL=20
# REGISTRY_CACHE_PROMPTS_TTL=15
# REGISTRY_CACHE_RESOURCES_TTL=15
# REGISTRY_CACHE_AGENTS_TTL=20
# REGISTRY_CACHE_SERVERS_TTL=20
# REGISTRY_CACHE_GATEWAYS_TTL=20
# REGISTRY_CACHE_CATALOG_TTL=300
# ADMIN_STATS_CACHE_SYSTEM_TTL=60
# ADMIN_STATS_CACHE_OBSERVABILITY_TTL=30
# ADMIN_STATS_CACHE_TAGS_TTL=120
# ADMIN_STATS_CACHE_PLUGINS_TTL=120
# ADMIN_STATS_CACHE_PERFORMANCE_TTL=60
# TEAM_MEMBER_COUNT_CACHE_TTL=300
# METRICS_CACHE_TTL_SECONDS=60
# Tool + pagination caches
# TOOL_LOOKUP_CACHE_TTL_SECONDS=60
# TOOL_LOOKUP_CACHE_NEGATIVE_TTL_SECONDS=10
# PAGINATION_COUNT_CACHE_TTL=300
# Session / message / global caches
# SESSION_TTL=3600
# MESSAGE_TTL=600
# GLOBAL_CONFIG_CACHE_TTL=60
# A2A_STATS_CACHE_TTL=30
# MCP_SESSION_POOL_TTL=300.0
# LLM chat caches
# LLMCHAT_SESSION_TTL=300
# LLMCHAT_SESSION_LOCK_TTL=30
# LLMCHAT_CHAT_HISTORY_TTL=3600
# Catalog / DCR / performance caches
# MCPGATEWAY_CATALOG_CACHE_TTL=3600
# DCR_METADATA_CACHE_TTL=3600
# MCPGATEWAY_PERFORMANCE_NET_CONNECTIONS_CACHE_TTL=15
# Static resource caching
# RESOURCE_CACHE_TTL=3600
# Redis leader election
# REDIS_LEADER_TTL=15
# REDIS_LEADER_HEARTBEAT_INTERVAL=5
# -----------------------------------------------------------------------------
# Pooling, concurrency, and limits
# -----------------------------------------------------------------------------
# Database connection pool (SQLAlchemy)
# DB_POOL_CLASS=auto
# DB_POOL_PRE_PING=auto
# DB_POOL_SIZE=200
# DB_MAX_OVERFLOW=10
# DB_POOL_TIMEOUT=30
# DB_POOL_RECYCLE=3600
# Redis connection pool
# REDIS_MAX_CONNECTIONS=50
# REDIS_SOCKET_TIMEOUT=2.0
# REDIS_SOCKET_CONNECT_TIMEOUT=2.0
# REDIS_HEALTH_CHECK_INTERVAL=30
# HTTPX shared client pool
# HTTPX_MAX_CONNECTIONS=200
# HTTPX_MAX_KEEPALIVE_CONNECTIONS=100
# HTTPX_KEEPALIVE_EXPIRY=30.0
# HTTPX_POOL_TIMEOUT=10.0
# Tool and federation limits
# TOOL_TIMEOUT=60
# MAX_TOOL_RETRIES=3
# TOOL_RATE_LIMIT=100
# TOOL_CONCURRENT_LIMIT=10
# FEDERATION_TIMEOUT=120
# Health checks
# HEALTH_CHECK_INTERVAL=60
# HEALTH_CHECK_TIMEOUT=5
# UNHEALTHY_THRESHOLD=3
# GATEWAY_VALIDATION_TIMEOUT=5
# MAX_CONCURRENT_HEALTH_CHECKS=10
# MCP session pool (client sessions)
# MCP_SESSION_POOL_ENABLED=false
# MCP_SESSION_POOL_MAX_PER_KEY=10
# MCP_SESSION_POOL_HEALTH_CHECK_INTERVAL=60.0
# MCP_SESSION_POOL_ACQUIRE_TIMEOUT=30.0
# MCP_SESSION_POOL_CREATE_TIMEOUT=30.0
# MCP_SESSION_POOL_CIRCUIT_BREAKER_THRESHOLD=5
# MCP_SESSION_POOL_CIRCUIT_BREAKER_RESET=60.0
# MCP_SESSION_POOL_IDLE_EVICTION=600.0
# MCP_SESSION_POOL_TRANSPORT_TIMEOUT=30.0
# MCP_SESSION_POOL_EXPLICIT_HEALTH_RPC=false
# MCP_SESSION_POOL_HEALTH_CHECK_METHODS=["ping", "skip"]
# MCP_SESSION_POOL_HEALTH_CHECK_TIMEOUT=5.0
# -----------------------------------------------------------------------------
# Timeouts, polling, and backoff
# -----------------------------------------------------------------------------
# Session registry polling (cache_type=database)
# POLL_INTERVAL=1.0
# MAX_INTERVAL=5.0
# BACKOFF_FACTOR=1.5
# DB startup resilience
# DB_MAX_RETRIES=30
# DB_RETRY_INTERVAL_MS=2000
# DB_MAX_BACKOFF_SECONDS=30
# Redis startup resilience
# REDIS_MAX_RETRIES=30
# REDIS_RETRY_INTERVAL_MS=2000
# REDIS_MAX_BACKOFF_SECONDS=30
# -----------------------------------------------------------------------------
# Retention and cleanup windows
# -----------------------------------------------------------------------------
# METRICS_RETENTION_DAYS=7
# METRICS_CLEANUP_INTERVAL_HOURS=1
# METRICS_ROLLUP_ENABLED=true
# METRICS_ROLLUP_INTERVAL_HOURS=1
# METRICS_ROLLUP_RETENTION_DAYS=365
# METRICS_ROLLUP_LATE_DATA_HOURS=1
# METRICS_DELETE_RAW_AFTER_ROLLUP=true
# METRICS_DELETE_RAW_AFTER_ROLLUP_HOURS=1
# MCPGATEWAY_PERFORMANCE_RETENTION_HOURS=24
# MCPGATEWAY_PERFORMANCE_RETENTION_DAYS=90
# OBSERVABILITY_TRACE_RETENTION_DAYS=7
# LOG_RETENTION_DAYS=30
# HTTPX timeouts
# HTTPX_CONNECT_TIMEOUT=5.0
# HTTPX_READ_TIMEOUT=120.0
# HTTPX_WRITE_TIMEOUT=30.0
# HTTPX_ADMIN_READ_TIMEOUT=30.0
# SSE / cancellation protection
# SSE_SEND_TIMEOUT=30.0
# SSE_RAPID_YIELD_WINDOW_MS=1000
# SSE_RAPID_YIELD_MAX=50
# MCP_SESSION_POOL_CLEANUP_TIMEOUT=5.0
# SSE_TASK_GROUP_CLEANUP_TIMEOUT=5.0
# ANYIO_CANCEL_DELIVERY_PATCH_ENABLED=false
# ANYIO_CANCEL_DELIVERY_MAX_ITERATIONS=100
# -----------------------------------------------------------------------------
# Middleware overhead and compression
# -----------------------------------------------------------------------------
# COMPRESSION_ENABLED=true
# COMPRESSION_GZIP_LEVEL=6
# COMPRESSION_BROTLI_QUALITY=4
# COMPRESSION_ZSTD_LEVEL=3
# COMPRESSION_MINIMUM_SIZE=500
# VALIDATION_MIDDLEWARE_ENABLED=false
# CORRELATION_ID_ENABLED=true
# TEMPLATES_AUTO_RELOAD=false
# STRUCTURED_LOGGING_DATABASE_ENABLED=false
# AUDIT_TRAIL_ENABLED=false
# SECURITY_LOGGING_ENABLED=false
# =============================================================================
# Basic Server Configuration
# =============================================================================
# Application name displayed in UI and logs
# APP_NAME=ContextForge
# Host interface to bind to (127.0.0.1 = localhost only)
# Project defaults block sets HOST=0.0.0.0 for local containers
# HOST=127.0.0.1
# Port number for the HTTP server
# PORT=4444
# Runtime environment - affects CORS, cookies, and security defaults
# Options: development, staging, production
# - development: Relaxed CORS (localhost:3000/8080), debug info, insecure cookies
# - staging: Production-like CORS and cookie defaults, but use staging domains
# - production: Strict CORS (APP_DOMAIN only), secure cookies, no debug info
# ENVIRONMENT=development
# Domain name for CORS origins and cookie settings (use your actual domain in production)
# Project defaults block sets APP_DOMAIN=http://localhost for local dev
# APP_DOMAIN=http://localhost:4444
# FastAPI root_path for reverse proxy deployments (empty = serve from root "/")
# Used when gateway is behind a proxy with path prefix (e.g., "/api/v1")
# See FastAPI docs: https://fastapi.tiangolo.com/advanced/behind-a-proxy/
# APP_ROOT_PATH=
# Client mode for gateway-as-client usage
# Options: true, false (default)
# CLIENT_MODE=false
# Override templates/static directories (absolute paths)
# Leave unset to use package defaults
# TEMPLATES_DIR=/absolute/path/to/templates
# STATIC_DIR=/absolute/path/to/static
# Enable HTTP Basic Auth for OpenAPI docs endpoints (/docs, /redoc)
# Options: true, false (default: false)
# When true: Allows accessing docs with BASIC_AUTH_USER/BASIC_AUTH_PASSWORD
# When false: Only JWT Bearer token authentication is accepted
# DOCS_ALLOW_BASIC_AUTH=false
# Database Configuration
# SQLite (default) - good for development and small deployments
# macOS note: If you see "sqlite3.OperationalError: disk I/O error" on macOS when running
# `make serve`, move the DB to a safe APFS path (avoid iCloud/Dropbox/OneDrive/Google Drive,
# network shares, or external exFAT) and use an absolute path, for example:
# DATABASE_URL=sqlite:////Users/$USER/Library/Application Support/mcpgateway/mcp.db
# DATABASE_URL=sqlite:///./mcp.db
# PostgreSQL - recommended for production deployments
# Uses psycopg3 driver (psycopg[binary])
# IMPORTANT: Use postgresql+psycopg:// (not postgresql://) for psycopg3
# DATABASE_URL=postgresql+psycopg://postgres:mysecretpassword@localhost:5432/mcp
# Database Connection Pool Configuration
# ============================================================================
# IMPORTANT: Pool size depends on your database connection strategy:
#
# WITH PgBouncer (recommended for PostgreSQL, default in docker-compose):
# - Use SMALL pool (10-20) since PgBouncer handles connection pooling
# - docker-compose.yml sets DB_POOL_SIZE=15 by default
# - Do NOT override here unless you know what you're doing
# - Formula: (replicas × workers × pool) should be < PgBouncer MAX_CLIENT_CONN
#
# WITHOUT PgBouncer (direct PostgreSQL or SQLite):
# - Use LARGER pool based on: (replicas × workers × pool) < max_connections
# - Uncomment and set DB_POOL_SIZE=50-200 depending on workload
#
# Uncomment for SQLite or direct PostgreSQL without PgBouncer
# DB_POOL_SIZE=200
# ============================================================================
# Additional connections beyond pool_size for burst handling (default: 10)
# DB_MAX_OVERFLOW=10
# Seconds to wait for connection before timeout (default: 30)
# DB_POOL_TIMEOUT=30
# Seconds before recreating connection to prevent stale connections (default: 3600)
# DB_POOL_RECYCLE=3600
# Database driver identifier (advanced; used by SQLAlchemy engine selection)
# DB_DRIVER=postgresql+psycopg
# Connection pool class selection
# Options: auto (default), null, queue
# DB_POOL_CLASS=auto
# Connection pool pre-ping behavior
# Options: auto (default), true, false
# DB_POOL_PRE_PING=auto
# Database Startup Resilience (exponential backoff with jitter)
# Retry progression: 2s → 4s → 8s → 16s → 30s (capped), ±25% jitter
# 30 retries ≈ 5 minutes total wait before worker gives up
# DB_MAX_RETRIES=30
# Base retry interval in milliseconds (doubles each attempt)
# DB_RETRY_INTERVAL_MS=2000
# Maximum backoff cap in seconds (jitter ±25% applied after cap)
# DB_MAX_BACKOFF_SECONDS=30
# psycopg3: Number of query executions before auto-preparing server-side (default: 5)
# Set to 0 to disable, 1 to prepare immediately. Higher values reduce memory usage.
# DB_PREPARE_THRESHOLD=5
# SQLite Configuration
# SQLite busy timeout (milliseconds) - maximum time SQLite will block while waiting
# to acquire a database lock before returning SQLITE_BUSY. Limits lock-wait latency
# and prevents prolonged thread blocking under write contention (default: 5000ms)
# DB_SQLITE_BUSY_TIMEOUT=5000
# Database Performance Optimization
# Use database-native percentile functions for observability performance metrics
# When true: PostgreSQL uses native percentile_cont (5-10x faster for large datasets)
# When false: Falls back to Python-based percentile calculations (works with all databases)
# Recommended: true for PostgreSQL production deployments, auto-detected for SQLite
# USE_POSTGRESDB_PERCENTILES=true
# The number of rows fetched from the database at a time when streaming results,
# to limit memory usage and avoid loading all rows into RAM at once.
# YIELD_BATCH_SIZE=1000
# Cache Backend Configuration
# Options: database (default), memory (in-process), redis (distributed)
# - database: Uses SQLite/PostgreSQL for persistence (good for single-node)
# - memory: Fast in-process caching (lost on restart, not shared between workers)
# - redis: Distributed caching for multi-node deployments
# REQUIRED for multi-worker session affinity (MCPGATEWAY_SESSION_AFFINITY_ENABLED=true)
# CACHE_TYPE=database
# Session Registry Database Polling (Adaptive Backoff)
# When CACHE_TYPE=database, sessions use polling to check for messages.
# Adaptive backoff reduces database load by ~90% during idle periods while
# maintaining responsiveness when messages arrive.
#
# How it works:
# - Starts polling at POLL_INTERVAL (1.0s default)
# - When no messages found, interval increases by BACKOFF_FACTOR (1.5x)
# - Backs off until reaching MAX_INTERVAL (5.0s cap)
# - Immediately resets to POLL_INTERVAL when a message arrives
#
# Example progression: 1.0s → 1.5s → 2.25s → 3.375s → 5.0s (capped)
# =============================================================================
# Tuning guide:
# - Lower POLL_INTERVAL (0.1-0.5s) for real-time applications needing <1s latency
# - Higher MAX_INTERVAL (10-30s) for batch workloads to minimize DB queries
# - Higher BACKOFF_FACTOR (2.0) for faster backoff, lower (1.2) for gradual
# POLL_INTERVAL=1.0
# MAX_INTERVAL=5.0
# BACKOFF_FACTOR=1.5
# Redis connection URL (only used when CACHE_TYPE=redis)
# Format: redis://[username:password@]host:port/database
# Example: redis://localhost:6379/0 (local), redis://redis:6379/0 (container)
# REDIS_URL=redis://localhost:6379/0
# Cache key prefix for Redis (used to namespace keys in shared Redis instances)
# Default: "mcpgw:"
# CACHE_PREFIX=mcpgw:
# Session time-to-live in seconds (how long sessions remain valid)
# Default: 3600 (1 hour)
# SESSION_TTL=3600
# Message time-to-live in seconds (how long messages are retained)
# Default: 600 (10 minutes)
# MESSAGE_TTL=600
# Redis Startup Resilience (exponential backoff with jitter)
# Same behavior as DB retries: 2s → 4s → 8s → 16s → 30s (capped), ±25% jitter
# 30 retries ≈ 5 minutes total wait before worker gives up
# REDIS_MAX_RETRIES=30
# Base retry interval in milliseconds (doubles each attempt)
# REDIS_RETRY_INTERVAL_MS=2000
# Maximum backoff cap in seconds (jitter ±25% applied after cap)
# REDIS_MAX_BACKOFF_SECONDS=30
# =============================================================================
# Redis Connection Pool - Performance Tuned
# =============================================================================
# Connection pool size per worker process
# Formula: (concurrent_requests / workers) * 1.5
# Default 50 handles ~500 concurrent requests with 10 workers
# REDIS_MAX_CONNECTIONS=50
# Socket read/write timeout (seconds)
# Keep low for fast failure detection; Redis ops typically <100ms
# REDIS_SOCKET_TIMEOUT=2.0
# Connection establishment timeout (seconds)
# Keep low to avoid blocking event loop on network issues
# REDIS_SOCKET_CONNECT_TIMEOUT=2.0
# Retry commands that timeout (recommended: true)
# REDIS_RETRY_ON_TIMEOUT=true
# Connection health check interval (seconds, 0=disabled)
# Prevents stale connections in pool
# REDIS_HEALTH_CHECK_INTERVAL=30
# Return strings instead of bytes (recommended: true)
# REDIS_DECODE_RESPONSES=true
# =============================================================================
# Redis Parser Configuration (Performance - ADR-026)
# =============================================================================
# Redis protocol parser selection
# Options:
# - auto (default): Use hiredis C parser if available, fallback to pure-Python
# - hiredis: Require hiredis C parser (fails if not installed)
# - python: Force pure-Python parser (useful for debugging)
#
# Performance benchmarks (hiredis vs pure-Python):
# - Simple SET/GET: ~1.1x faster
# - LRANGE (10 items): ~2.7x faster
# - LRANGE (100 items): ~10x faster
# - LRANGE (999 items): ~83x faster
#
# Recommendation: Leave as "auto" - hiredis is installed by default with redis[hiredis]
# REDIS_PARSER=auto
# =============================================================================
# Redis Leader Election - Multi-Node Deployments
# =============================================================================
# Leader TTL in seconds (time before failover if leader dies)
# Lower = faster failover, but more sensitive to network blips
# Recommended: 15s for production, 5s for development
# REDIS_LEADER_TTL=15
# Leader heartbeat interval (seconds)
# Must be < leader_ttl/2 to prevent false failovers
# Rule: heartbeat_interval <= leader_ttl / 3
# REDIS_LEADER_HEARTBEAT_INTERVAL=5
# Leader key name in Redis
# REDIS_LEADER_KEY=gateway_service_leader
# =============================================================================
# Protocol Settings
# =============================================================================
# MCP protocol version supported by this gateway
# PROTOCOL_VERSION=2025-06-18
# =============================================================================
# Authentication
# =============================================================================
# Admin UI HTTP Basic Auth credentials
# Used for: Admin UI login, /docs endpoint (if DOCS_ALLOW_BASIC_AUTH=true)
# PRODUCTION: Change these to strong, unique values!
# BASIC_AUTH_USER=admin
# BASIC_AUTH_PASSWORD=changeme
# Global authentication requirement
# Options: true (default), false
# When true: All endpoints require authentication (Basic or JWT)
# When false: Endpoints are publicly accessible (NOT RECOMMENDED)
# AUTH_REQUIRED=true
# MCP endpoint authentication requirement
# Options: true, false
# Default when unset: follows AUTH_REQUIRED
# - AUTH_REQUIRED=true -> MCP auth required
# - AUTH_REQUIRED=false -> public-only /mcp access allowed
# Set to false explicitly to allow unauthenticated public-only MCP access.
# MCP_REQUIRE_AUTH=true
# JWT Algorithm Selection
# Supported algorithms:
# HMAC (Symmetric): HS256, HS384, HS512 - Simple deployments, shared secret
# RSA (Asymmetric): RS256, RS384, RS512 - Enterprise, distributed systems
# ECDSA (Asymmetric): ES256, ES384, ES512 - High performance, modern crypto
# JWT_ALGORITHM=HS256
# === HMAC (Symmetric) Configuration - Default for Development ===
# Secret used to sign JWTs (required for HMAC algorithms: HS256, HS384, HS512)
# PRODUCTION: Use a strong, random secret (minimum 32 characters)
# Generate with: openssl rand -base64 32
# JWT_SECRET_KEY=my-test-key-but-now-longer-than-32-bytes
# === RSA/ECDSA (Asymmetric) Configuration - Recommended for Production ===
# Public and private key paths (required for asymmetric algorithms: RS*, ES*)
# Generate RSA keys with: make certs-jwt
# (creates certs/jwt/private.pem and certs/jwt/public.pem with proper permissions)
# Generate ECDSA keys with: make certs-jwt-ecdsa
# (creates certs/jwt/ec_private.pem and certs/jwt/ec_public.pem with proper permissions)
# Generate both SSL and JWT keys: make certs-all
#JWT_PUBLIC_KEY_PATH=certs/jwt/public.pem
#JWT_PRIVATE_KEY_PATH=certs/jwt/private.pem
# JWT Claims Configuration
# PRODUCTION: Set these to your service-specific values
# JWT_AUDIENCE=mcpgateway-api
# JWT_ISSUER=mcpgateway
# JWT Validation Options
# Set to false for Dynamic Client Registration (DCR) scenarios where audience varies
# JWT_AUDIENCE_VERIFICATION=true
# Set to false for custom auth flows where issuer varies or is not present
# JWT_ISSUER_VERIFICATION=true
# Expiry time for generated JWT tokens (in minutes; e.g. 7 days)
# TOKEN_EXPIRY=10080
# SECURITY: Require expiration claim in all tokens (default: true)
# Set to false only for backward compatibility with legacy tokens
# REQUIRE_TOKEN_EXPIRATION=true
# SECURITY: Require JTI (JWT ID) claim for token revocation support (default: true)
# Set to false only for backward compatibility with legacy tokens
# REQUIRE_JTI=true
# Require all authenticated users to exist in the database
# When true, disables the platform admin bootstrap mechanism
# WARNING: Enabling this on a fresh deployment will lock you out!
# REQUIRE_USER_IN_DB=false
# Embed environment claim in gateway-issued JWTs
# EMBED_ENVIRONMENT_IN_TOKENS=false
# Reject tokens with mismatched environment claim (tokens without env are allowed)
# VALIDATE_TOKEN_ENVIRONMENT=false
# =============================================================================
# Security Validation & Sanitization
# =============================================================================
# Enable experimental input validation and output sanitization
# This implements gateway-level security controls to protect against:
# - Path traversal attacks (../../../etc/passwd)
# - Command injection (file.jpg; rm -rf /)
# - SQL injection ('; DROP TABLE users; --)
# - XSS attacks (<script>alert(1)</script>)
# - Control character injection (\x1b[31m)
#
# Roll-out phases:
# Phase 0: EXPERIMENTAL_VALIDATE_IO=false (disabled, default)
# Phase 1: EXPERIMENTAL_VALIDATE_IO=true, VALIDATION_STRICT=false (log-only)
# Phase 2: EXPERIMENTAL_VALIDATE_IO=true, VALIDATION_STRICT=true (enforce in staging)
# Phase 3: Production deployment with all features enabled
# Project defaults block enables EXPERIMENTAL_VALIDATE_IO for local dev
# EXPERIMENTAL_VALIDATE_IO=false
# Enable validation middleware for all requests
# When enabled, validates all incoming request parameters and paths
# Options: true, false (default)
# Project defaults block enables VALIDATION_MIDDLEWARE_ENABLED for local dev
# VALIDATION_MIDDLEWARE_ENABLED=false
# Strict validation mode
# Options:
# - true: Reject requests with validation failures (422 status)
# - false: Log warnings but allow requests (log-only mode)
# Recommended: false for dev/staging, true for production
#
# Note: this also controls the forbidden-pattern check on tool descriptions
# during registration (shell metacharacters such as "> ", "< ", "|", ";").
# Set to false if your MCP server tools have Markdown-formatted descriptions
# that contain these characters (e.g. "> blockquote", "< input", "cmd | grep").
# For more targeted control, see TOOL_DESCRIPTION_FORBIDDEN_PATTERNS below.
# VALIDATION_STRICT=true
# Tool description forbidden pattern validation
# Master switch to enable/disable forbidden pattern checks on tool descriptions.
# When disabled, no pattern checks are performed regardless of VALIDATION_STRICT.
# Options: true (default), false
# TOOL_DESCRIPTION_FORBIDDEN_PATTERNS_ENABLED=true
# Override the list of substrings blocked in tool descriptions.
# Accepts a JSON array. Default: ["&&", ";", "||", "$(", "|", "> ", "< "]
# Set to a custom list to allow specific patterns while still blocking others.
# Example: TOOL_DESCRIPTION_FORBIDDEN_PATTERNS=["&&", "$("]
# TOOL_DESCRIPTION_FORBIDDEN_PATTERNS=["&&", ";", "||", "$(", "|", "> ", "< "]
# Strict JSON Schema validation for tools and prompts
# Options:
# - true: Reject invalid JSON schemas during registration (strict spec compliance)
# - false: Log warnings only (backward compatibility for legacy tools)
# JSON_SCHEMA_VALIDATION_STRICT=true
# Sanitize output to remove control characters
# Removes ANSI escape sequences and C0/C1 control characters from responses
# Preserves newlines (\n) and tabs (\t)
# Options: true (default), false
# SANITIZE_OUTPUT=true
# Allowed root paths for resource access
# Restricts file system access to specific directories
# Format: JSON array or comma-separated list
# Examples:
# - JSON: ["/srv/data", "/var/app/uploads"]
# - CSV: /srv/data,/var/app/uploads
# - Empty: [] (no restrictions, not recommended)
# PRODUCTION: Always configure this to limit resource access
# ALLOWED_ROOTS=[]
# Maximum allowed path depth
# Prevents deeply nested path attacks
# Default: 10 levels
# MAX_PATH_DEPTH=10
# Maximum parameter length (characters)
# Prevents buffer overflow and DoS attacks
# Default: 10000 characters
# MAX_PARAM_LENGTH=10000
# CWE-400: Limits for user-supplied meta_data forwarded to upstream MCP servers.
# Keeps arbitrarily large dicts from amplifying into downstream network/DB load.
# Maximum number of top-level keys in meta_data (default: 16)
# META_MAX_KEYS=16
# Maximum nesting depth in meta_data (default: 2)
# META_MAX_DEPTH=2
# Maximum JSON-encoded byte size of meta_data (default: 4096)
# META_MAX_BYTES=4096
# Regex patterns for dangerous input (JSON array)
# Used to detect and block malicious input patterns
# Default patterns:
# 1. Shell metacharacters: [;&|`$(){}\[\]<>]
# 2. Path traversal: \.\.[/\\]
# 3. Control characters: [\x00-\x1f\x7f-\x9f]
# Format: JSON array of regex patterns
# Project defaults block adds an SQL injection pattern on top of defaults
# DANGEROUS_PATTERNS=["[;&|`$(){}\\[\\]<>]", "\\.\\.[\\\\/]", "[\\x00-\\x1f\\x7f-\\x9f]"]
# =============================================================================
# Email-Based Authentication
# =============================================================================
# Enable email-based authentication system
# EMAIL_AUTH_ENABLED=true
# Public registration control
# When false (default), only admins can create user accounts via /admin/users
# When true, anyone can self-register via /auth/email/register
# SECURITY: Keep this false in production unless you explicitly need public sign-up
# PUBLIC_REGISTRATION_ENABLED=false
# Admin protection mode
# When true (default), no admin can be demoted, deactivated, or locked out via API/UI
# When false, only the last remaining active admin is protected
# PROTECT_ALL_ADMINS=true
# Platform admin user (bootstrap from environment)
# PRODUCTION: Change these to your actual admin credentials!
# PLATFORM_ADMIN_EMAIL=admin@example.com
# PLATFORM_ADMIN_PASSWORD=changeme
# PLATFORM_ADMIN_FULL_NAME=Platform Administrator
# Default password for newly created users (bootstrap only)
# DEFAULT_USER_PASSWORD=changeme
# Argon2id Password Hashing Configuration
# Time cost (iterations) - higher = more secure but slower
# ARGON2ID_TIME_COST=3
# Memory cost (KB) - higher = more secure but uses more RAM
# ARGON2ID_MEMORY_COST=65536
# Parallelism (threads) - typically 1 for web apps
# ARGON2ID_PARALLELISM=1
# Password Policy Configuration
# PASSWORD_MIN_LENGTH=8
# Project defaults block relaxes these for local bootstrap
# PASSWORD_REQUIRE_UPPERCASE=true
# PASSWORD_REQUIRE_LOWERCASE=true
# PASSWORD_REQUIRE_NUMBERS=false
# PASSWORD_REQUIRE_SPECIAL=true
# Password Change Enforcement
# Master switch for all password change enforcement checks
# PASSWORD_CHANGE_ENFORCEMENT_ENABLED=true
# Force admin to change password after bootstrap
# ADMIN_REQUIRE_PASSWORD_CHANGE_ON_BOOTSTRAP=true
# Detect default password during login and mark user for change
# DETECT_DEFAULT_PASSWORD_ON_LOGIN=true
# Require password change when using default password
# REQUIRE_PASSWORD_CHANGE_FOR_DEFAULT_PASSWORD=true
# Enable password complexity validation for new/changed passwords
# PASSWORD_POLICY_ENABLED=true
# Prevent reusing the current password when changing
# PASSWORD_PREVENT_REUSE=true
# Password maximum age in days before expiry forces a change
# PASSWORD_MAX_AGE_DAYS=90
# Account Security Configuration
# Maximum failed login attempts before account lockout
# MAX_FAILED_LOGIN_ATTEMPTS=10
# Account lockout duration in minutes
# ACCOUNT_LOCKOUT_DURATION_MINUTES=1
# Send lockout notification emails when an account is locked
# ACCOUNT_LOCKOUT_NOTIFICATION_ENABLED=true
# Minimum response time for failed login attempts (milliseconds)
# Helps reduce timing-based account enumeration
# FAILED_LOGIN_MIN_RESPONSE_MS=250
# Self-Service Password Reset
# Enable forgot-password and reset-password workflows
# Set to false to disable public self-service reset UI/API endpoints.
# PASSWORD_RESET_ENABLED=true
# Password reset token validity (minutes)
# PASSWORD_RESET_TOKEN_EXPIRY_MINUTES=60
# Max password reset requests allowed per email in each window
# PASSWORD_RESET_RATE_LIMIT=5
# Rate limit window length (minutes)
# PASSWORD_RESET_RATE_WINDOW_MINUTES=15
# Invalidate active sessions after successful password reset
# PASSWORD_RESET_INVALIDATE_SESSIONS=true
# Minimum response time for forgot-password requests (milliseconds)
# Helps reduce timing-based account enumeration
# PASSWORD_RESET_MIN_RESPONSE_MS=250
# SMTP Email Delivery (for password reset + lockout notifications)
# Enable SMTP delivery
# SMTP_ENABLED=false
# SMTP_HOST=smtp.example.com
# SMTP_PORT=587
# SMTP_USER=noreply@example.com
# SMTP_PASSWORD=changeme
# SMTP_FROM_EMAIL=noreply@example.com
# SMTP_FROM_NAME=ContextForge
# Use STARTTLS
# SMTP_USE_TLS=true
# Use implicit SSL/TLS (set true for port 465)
# SMTP_USE_SSL=false
# SMTP_TIMEOUT_SECONDS=15
# MCP Client Authentication
# Controls JWT authentication for /mcp endpoints
# MCP_CLIENT_AUTH_ENABLED=true
# TRUST_PROXY_AUTH=false
# PROXY_USER_HEADER=X-Authenticated-User