-
Notifications
You must be signed in to change notification settings - Fork 6.2k
Expand file tree
/
Copy path.env.example
More file actions
743 lines (621 loc) · 33.6 KB
/
Copy path.env.example
File metadata and controls
743 lines (621 loc) · 33.6 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
# =============================================================================
# OpenMAIC Environment Variables
# Copy this file to .env.local and fill in the values you need.
# DATABASE_URL is required, except under docker-compose.yml, which sets it for
# the bundled PostgreSQL (see "Persistence" below). Everything else is optional.
#
# Models are configured in openmaic.yml (copy openmaic.example.yml), which
# references the keys below as ${VAR}, or in the model settings of the web app.
# The provider variables in this file, server-providers.yml, DEFAULT_MODEL and
# MODEL_FALLBACK are the legacy configuration: they still work while there is
# no openmaic.yml and are deprecated. See the Configuration docs.
# =============================================================================
# --- Model configuration file ------------------------------------------------
# Path of the model configuration. Default: openmaic.yml in the directory the
# server starts in (the repository root; /app in the Docker image). When set,
# the file must exist. Read at startup; restart after changing it.
# OPENMAIC_CONFIG=openmaic.yml
# --- Instance secret ---------------------------------------------------------
# Encrypts provider keys saved in the web settings. When unset, one is created
# on first start in data/instance-secret.key (the Docker data volume). Keep it
# with the database: saved keys cannot be read without it. Set it explicitly
# when running more than one instance, when data/ does not survive a restart,
# or on a read-only filesystem, e.g. from `openssl rand -base64 32`. The server
# warns at startup when stored keys were sealed under a different secret.
# Instances that share a database also share one persistent data directory
# (data/, /app/data in the image): uploaded materials are stored there.
# OPENMAIC_SECRET_KEY=
# --- LLM Providers -----------------------------------------------------------
# Format: {PROVIDER}_API_KEY, {PROVIDER}_BASE_URL (optional), {PROVIDER}_MODELS (optional, comma-separated)
# Legacy: without openmaic.yml these configure providers directly. With
# openmaic.yml they no longer configure providers on their own: reference them
# from the file as ${VAR} (for example apiKey: ${OPENAI_API_KEY}). The TTS,
# ASR, image, video, PDF and web search variables below work the same way.
OPENAI_API_KEY=
OPENAI_BASE_URL=
OPENAI_MODELS=
# For relays whose non-streaming Chat Completions response is incompatible.
# Forces custom OpenAI base URLs to use Chat Completions and buffers SSE responses.
# Has no effect on the official OpenAI base URL. Disabled by default.
# OPENAI_COMPAT_USE_STREAMING_CHAT=true
# Azure uses deployment names as model IDs.
AZURE_OPENAI_API_KEY=
AZURE_OPENAI_BASE_URL=https://YOUR-RESOURCE.openai.azure.com/openai
AZURE_OPENAI_MODELS=
ATLASCLOUD_API_KEY=
ATLASCLOUD_BASE_URL=https://api.atlascloud.ai/v1
# Example: qwen/qwen3.5-flash,deepseek-ai/deepseek-v4-pro
ATLASCLOUD_MODELS=
ANTHROPIC_API_KEY=
ANTHROPIC_BASE_URL=
ANTHROPIC_MODELS=
GOOGLE_API_KEY=
GOOGLE_BASE_URL=
GOOGLE_MODELS=
DEEPSEEK_API_KEY=
DEEPSEEK_BASE_URL=
# Example: deepseek-v4-pro,deepseek-v4-flash,deepseek-v4-flash-vision-exp
DEEPSEEK_MODELS=
QWEN_API_KEY=
QWEN_BASE_URL=
QWEN_MODELS=
KIMI_API_KEY=
KIMI_BASE_URL=
KIMI_MODELS=
MINIMAX_API_KEY=
# MiniMax Anthropic-compatible endpoint for the built-in Anthropic SDK integration
MINIMAX_BASE_URL=https://api.minimaxi.com/anthropic/v1
# Example: MiniMax-M2.7-highspeed,MiniMax-M2.7,MiniMax-M2.5-highspeed,MiniMax-M2.5
MINIMAX_MODELS=
GLM_API_KEY=
GLM_BASE_URL=
GLM_MODELS=
SILICONFLOW_API_KEY=
SILICONFLOW_BASE_URL=
SILICONFLOW_MODELS=
DOUBAO_API_KEY=
DOUBAO_BASE_URL=
DOUBAO_MODELS=
OPENROUTER_API_KEY=
OPENROUTER_BASE_URL=https://openrouter.ai/api/v1
# Example: deepseek/deepseek-v4-pro,deepseek/deepseek-v4-flash
OPENROUTER_MODELS=
GROK_API_KEY=
GROK_BASE_URL=
# Example: grok-4.6,grok-4.5
GROK_MODELS=
TENCENT_API_KEY=
# Tencent TokenHub OpenAI-compatible endpoint. Hy3 is a model ID, not an env prefix.
# TENCENT_HUNYUAN_* is also accepted as an alias.
TENCENT_BASE_URL=https://tokenhub.tencentmaas.com/v1
# Example: hy3-preview,hunyuan-2.0-thinking-20251109,hunyuan-2.0-instruct-20251111
TENCENT_MODELS=
XIAOMI_API_KEY=
# MIMO_* is also accepted as an alias. Use tp-... keys only with Token Plan URLs.
XIAOMI_BASE_URL=https://api.xiaomimimo.com/v1
# Token Plan regional examples:
# XIAOMI_BASE_URL=https://token-plan-cn.xiaomimimo.com/v1
# XIAOMI_BASE_URL=https://token-plan-sgp.xiaomimimo.com/v1
# XIAOMI_BASE_URL=https://token-plan-ams.xiaomimimo.com/v1
# Example: mimo-v2.6-pro,mimo-v2.6-flash,mimo-v2.5-pro,mimo-v2-pro,mimo-v2.5,mimo-v2-omni,mimo-v2-flash
XIAOMI_MODELS=
TOKENDANCE_API_KEY=
# OpenAI-compatible gateway. The same key also works for the image, video, TTS
# and web-search routes on this host (see the README quick example).
TOKENDANCE_BASE_URL=https://tokendance.space/gateway/v1
# Example: deepseek-v4.1-flash,deepseek-v4-pro,glm-5.3,kimi-k3,qwen3.8-max
TOKENDANCE_MODELS=
# --- Ollama (Local Models) ---------------------------------------------------
# No API key needed. Configure BASE_URL here (server-side) so it bypasses SSRF
# protection automatically. Client-supplied localhost URLs are blocked in production.
# OLLAMA_BASE_URL=http://localhost:11434/v1
# OLLAMA_MODELS=llama3.3,llama3.2,qwen2.5,mistral,gemma3
# Lemonade local server (OpenAI-compatible, no API key required)
# LEMONADE_BASE_URL=http://localhost:13305/v1
# LEMONADE_MODELS=Qwen3-0.6B-GGUF,Llama-3.2-1B-Instruct-Hybrid,Qwen2.5-VL-7B-Instruct
# Amazon Bedrock LLMs (no OpenAI-style API key required)
# Set BEDROCK_REGION to enable Bedrock server-side provider config.
# AWS credentials are resolved from the standard AWS environment / credential chain.
# BEDROCK_REGION=us-east-1
# BEDROCK_MODELS=us.anthropic.claude-sonnet-5,us.anthropic.claude-opus-4-8
# Optional bearer-token authentication or custom Bedrock-compatible endpoint.
# AWS_BEARER_TOKEN_BEDROCK=
# BEDROCK_API_KEY=
# BEDROCK_BASE_URL=
# DEFAULT_MODEL=bedrock:us.anthropic.claude-sonnet-5
# --- TTS (Text-to-Speech) ----------------------------------------------------
TTS_OPENAI_API_KEY=
TTS_OPENAI_BASE_URL=
TTS_AZURE_API_KEY=
TTS_AZURE_BASE_URL=
TTS_GLM_API_KEY=
TTS_GLM_BASE_URL=
TTS_QWEN_API_KEY=
TTS_QWEN_BASE_URL=
# Qwen voice cloning reuses TTS_QWEN_API_KEY. Override the target model if needed.
# TTS_QWEN_VOICE_CLONE_MODEL=qwen3-tts-vc-2026-01-22
TTS_DOUBAO_API_KEY=
TTS_DOUBAO_BASE_URL=
TTS_MINIMAX_API_KEY=
# MiniMax TTS endpoint (speech-2.8 / 2.6 / 02 / 01 series)
TTS_MINIMAX_BASE_URL=https://api.minimaxi.com
TTS_ELEVENLABS_API_KEY=
TTS_ELEVENLABS_BASE_URL=
# Google Gemini TTS (Interactions API). Reuse an AI Studio / Gemini API key.
TTS_GOOGLE_API_KEY=
TTS_GOOGLE_BASE_URL=
# VoxCPM2 TTS (local, OpenAI-compatible; API key is optional)
# TTS_VOXCPM_API_KEY=
# TTS_VOXCPM_BASE_URL=http://localhost:8000/v1
# Lemonade TTS (local, no API key required)
# TTS_LEMONADE_BASE_URL=http://localhost:13305/v1
# Operators can force-disable any built-in TTS provider. Examples:
# TTS_OPENAI_ENABLED=false
# TTS_BROWSER_NATIVE_ENABLED=false
# --- ASR (Automatic Speech Recognition) --------------------------------------
ASR_OPENAI_API_KEY=
ASR_OPENAI_BASE_URL=
ASR_QWEN_API_KEY=
ASR_QWEN_BASE_URL=
ASR_AZURE_API_KEY=
ASR_AZURE_BASE_URL=https://{region}.api.cognitive.microsoft.com
# FunASR (local, WAV input only, no API key required)
# ASR_FUNASR_BASE_URL=http://localhost:8000/v1
# Lemonade ASR (local, WAV input only, no API key required)
# ASR_LEMONADE_BASE_URL=http://localhost:13305/v1
# Operators can force-disable any built-in ASR provider. Examples:
# ASR_OPENAI_ENABLED=false
# ASR_BROWSER_NATIVE_ENABLED=false
# Optional local audio/video material extraction uses the first enabled server
# ASR provider above. It also requires the system `ffmpeg` and `ffprobe`
# executables on PATH; no bundled binary or npm dependency is installed.
# Without both executables, OpenMAIC skips the local extractor and uses a
# configured AliDocMind cloud extractor when available. With neither path
# enabled, media materials fail cleanly with setup guidance.
# --- PDF Processing -----------------------------------------------------------
PDF_UNPDF_API_KEY=
PDF_UNPDF_BASE_URL=
PDF_MINERU_API_KEY=
PDF_MINERU_BASE_URL=
# Optional. Defaults to "pipeline"; use "hybrid-auto-engine" only when your MinerU
# service has the required GPU/device configuration.
PDF_MINERU_BACKEND=
PDF_MINERU_CLOUD_API_KEY=
PDF_MINERU_CLOUD_BASE_URL=https://mineru.net/api/v4
# Self-hosted MinerU never falls back to MinerU Cloud implicitly: a request that
# selects self-hosted MinerU without a configured base URL fails loudly. Set this
# to "true" to explicitly opt in to MinerU Cloud as a fallback (documents then
# leave your infrastructure). Default: off.
ALLOW_MINERU_CLOUD_FALLBACK=
# AliDocMind uses an Alibaba Cloud AccessKey pair instead of a single API key.
ALIDOCMIND_ACCESS_KEY_ID=
ALIDOCMIND_ACCESS_KEY_SECRET=
ALIDOCMIND_BASE_URL=
# --- Image Generation ---------------------------------------------------------
IMAGE_OPENAI_API_KEY=
IMAGE_OPENAI_BASE_URL=https://api.openai.com/v1
IMAGE_SEEDREAM_API_KEY=
IMAGE_SEEDREAM_BASE_URL=
IMAGE_QWEN_IMAGE_API_KEY=
IMAGE_QWEN_IMAGE_BASE_URL=
IMAGE_NANO_BANANA_API_KEY=
IMAGE_NANO_BANANA_BASE_URL=
IMAGE_MINIMAX_API_KEY=
# Example models: image-01, image-01-live
IMAGE_MINIMAX_BASE_URL=https://api.minimaxi.com
IMAGE_GROK_API_KEY=
IMAGE_GROK_BASE_URL=
# OpenRouter image generation. Optional; read at runtime. One key reaches every
# image model OpenRouter hosts (FLUX, Seedream, GPT Image, Gemini, Qwen Image,
# Recraft, Krea, ...). The model list in Settings is fetched live from
# GET /images/models, so no model id is pinned here. Base URL defaults to
# https://openrouter.ai/api/v1 when left blank.
IMAGE_OPENROUTER_API_KEY=
IMAGE_OPENROUTER_BASE_URL=
# Lemonade image generation (local, no API key required)
# IMAGE_LEMONADE_BASE_URL=http://localhost:13305/v1
# Operators can force-disable any built-in image provider, including the
# client-only ComfyUI provider (it has no credential env). Examples:
# IMAGE_OPENAI_ENABLED=false
# IMAGE_COMFYUI_ENABLED=false
# --- Video Generation ---------------------------------------------------------
VIDEO_SEEDANCE_API_KEY=
VIDEO_SEEDANCE_BASE_URL=
VIDEO_KLING_API_KEY=
VIDEO_KLING_BASE_URL=
VIDEO_VEO_API_KEY=
VIDEO_VEO_BASE_URL=
VIDEO_SORA_API_KEY=
VIDEO_SORA_BASE_URL=
VIDEO_MINIMAX_API_KEY=
# Example models: MiniMax-Hailuo-2.3, MiniMax-Hailuo-2.3-Fast, MiniMax-Hailuo-02
VIDEO_MINIMAX_BASE_URL=https://api.minimaxi.com
VIDEO_GROK_API_KEY=
VIDEO_GROK_BASE_URL=
VIDEO_HAPPYHORSE_API_KEY=
VIDEO_HAPPYHORSE_BASE_URL=https://dashscope.aliyuncs.com
# OpenRouter video generation. Optional; read at runtime. One key reaches every
# video model OpenRouter hosts (Veo, Kling, Runway, Seedance, Hailuo, Wan,
# Sora, Grok Imagine, ...). The model list in Settings is fetched live from
# GET /videos/models, so no model id is pinned here. Base URL defaults to
# https://openrouter.ai/api/v1 when left blank.
VIDEO_OPENROUTER_API_KEY=
VIDEO_OPENROUTER_BASE_URL=
# Operators can force-disable any built-in video provider. Examples:
# VIDEO_GROK_ENABLED=false
# VIDEO_KLING_ENABLED=false
# --- Web Search ---------------------------------------------------------------
# Note: Grok (xAI) web search is available via chat completions + search tools,
# not as a standalone search API. Use Grok LLM provider with search_parameters
# in chat requests. See: https://docs.x.ai/docs/guides/tools/search-tools
TAVILY_API_KEY=
TAVILY_BASE_URL=
EXA_API_KEY=
EXA_BASE_URL=https://api.exa.ai
BOCHA_API_KEY=
BOCHA_BASE_URL=https://api.bocha.cn
BRAVE_API_KEY=
BRAVE_BASE_URL=
BAIDU_API_KEY=
BAIDU_BASE_URL=https://qianfan.baidubce.com
# Self-hosted SearXNG instance (no API key required)
SEARXNG_BASE_URL=
# Dedicated MiniMax web-search vars avoid conflicting with the LLM MINIMAX_* endpoint.
WEB_SEARCH_MINIMAX_API_KEY=
WEB_SEARCH_MINIMAX_BASE_URL=https://api.minimaxi.com
# Dedicated Doubao web-search vars avoid conflicting with the Doubao LLM provider.
WEB_SEARCH_DOUBAO_API_KEY=
WEB_SEARCH_DOUBAO_BASE_URL=https://open.feedcoopapi.com
# Claude (Anthropic) native web search. Dedicated vars avoid conflicting with
# ANTHROPIC_* LLM provider vars. Optional WEB_SEARCH_CLAUDE_MODELS pins the
# search model server-side (first entry wins), e.g. claude-sonnet-5.
WEB_SEARCH_CLAUDE_API_KEY=
WEB_SEARCH_CLAUDE_BASE_URL=https://api.anthropic.com/v1
WEB_SEARCH_CLAUDE_MODELS=
# Operators can force-disable any built-in web search provider. Examples:
# TAVILY_ENABLED=false
# EXA_ENABLED=false
# WEB_SEARCH_DOUBAO_ENABLED=false
# SEARXNG_ENABLED=false
# Server-only, default-OFF selector for the Native Child execution harness.
# OPENMAIC_ENABLE_PI_NATIVE_CHILD_RUNTIME=true
# Server-only, default-OFF Native Spotlight capability; does not select the runtime.
# OPENMAIC_ENABLE_PI_NATIVE_CHILD_SPOTLIGHT=true
# --- Feature Flags -----------------------------------------------------------
# Boolean feature flags accept "true" or "1". NEXT_PUBLIC_* values are compiled
# into the browser bundle at build time, so changing them requires a rebuild.
# Enable the Pro workbench entry (the workbench also requires the agent
# runtime to be configured server-side; see the Agent Runtime section).
# Implies the MAIC Editor gate below — Pro mode always ships with the editor.
# NEXT_PUBLIC_PRO_WORKBENCH_ENABLED=true
# Master gate for the MAIC Editor Pro-mode entry point. Implied by
# NEXT_PUBLIC_PRO_WORKBENCH_ENABLED; set it alone to enable the classroom
# editor on a deployment without the workbench.
# NEXT_PUBLIC_MAIC_EDITOR_ENABLED=true
# Select @openmaic/editor inside Pro mode. This does not enable Pro mode by itself.
# NEXT_PUBLIC_MAIC_EDITOR_RENDERER_ENABLED=true
# Use @openmaic/renderer for the classroom playback canvas.
# NEXT_PUBLIC_MAIC_PLAYBACK_RENDERER_ENABLED=true
# Pi-based classroom chat is enabled by default. Set this build-time flag to
# false or 0 and rebuild to roll back to the legacy classroom chat runtime.
# Pi requires model/provider tool (function) calling support.
# Docker Compose's env_file loads .env.local at runtime, not into build args.
# To rebuild with legacy chat: NEXT_PUBLIC_PI_CHAT_ENABLED=false docker compose up -d --build openmaic
# Runtime-only changes leave the built client/server choice unchanged.
# NEXT_PUBLIC_PI_CHAT_ENABLED=false
# Enable the unified PPT/Interactive courseware-reference entry in Pi playback.
# Pi chat and editor element references remain independent from this default-off build-time gate.
# Changing a NEXT_PUBLIC_* value requires rebuilding the application.
# NEXT_PUBLIC_COURSEWARE_REFERENCE_ENABLED=true
# Enable the server-side vocational task-engine generation path.
# OPENMAIC_ENABLE_VOCATIONAL=true
# Show the experimental vocational task-engine control in the client.
# NEXT_PUBLIC_SHOW_VOCATIONAL_TEST_UI=true
# Show the video export and PPTX import entry points.
# NEXT_PUBLIC_ENABLE_VIDEO_EXPORT=true
# NEXT_PUBLIC_ENABLE_PPTX_IMPORT=true
# Informational destination shown on exported Quiz/PBL cover cards. Unset or
# blank defaults to open.maic.chat; set to "off" to omit it.
# NEXT_PUBLIC_VIDEO_EXPORT_CTA_DESTINATION=open.maic.chat
# --- Agent Runtime (experimental) ---------------------------------------------
# Server-only gate for durable background agent sessions: the /api/agent
# session and owner-event control-plane routes plus the in-process session
# runner. Default OFF — while disabled, every /api/agent/sessions* and
# /api/agent/owner-events route answers 404. Truthy values are "true" or "1";
# anything else (including unset) is treated as disabled.
# OPENMAIC_AGENT_RUNTIME_ENABLED=true
# The runtime uses the PostgreSQL connection (DATABASE_URL) from the
# "Persistence" section below, which every deployment configures.
# The agent runs on the `agent` slot of the model configuration (openmaic.yml,
# or the model settings): a model that supports tool calling. Optional fields
# on the slot: api ("openai-completions", the default, or "openai-responses"),
# contextWindow (pins the window compaction assumes below the catalogue value),
# and thinking, which must never set effort (the tool-using driver cannot
# combine reasoning_effort with function tools on this transport). A
# deployment configured only through DEFAULT_MODEL has the agent turned off.
# Runner tuning. Defaults are shown; only relevant once the runtime is enabled.
# OPENMAIC_AGENT_RUNTIME_SCAN_INTERVAL_MS=1000
# OPENMAIC_AGENT_RUNTIME_HEARTBEAT_MS=2000
# OPENMAIC_AGENT_RUNTIME_LEASE_TTL_MS=10000
# OPENMAIC_AGENT_RUNTIME_MAX_CONCURRENT=2
# OPENMAIC_AGENT_RUNTIME_MAX_ATTEMPTS=5
# Global per-tool-call execution bound for every agent run (ms). A tool call
# that neither resolves nor rejects within the budget is aborted and settles as
# an error tool-result the agent can retry or proceed from; the session does
# not die. Default 600000 (10 minutes). Tools with known longer budgets (media
# synthesis, material extraction) carry their own explicit bounds in code.
# OPENMAIC_AGENT_TOOL_TIMEOUT_MS=600000
# Conversation compaction is reserved and OFF by default. The reusable
# compaction runtime is not implemented yet — it lands in a later slice of
# work — and until then the runner runs without context transformation, so
# these knobs are inert placeholders.
# OPENMAIC_AGENT_COMPACTION_ENABLED=true
# OPENMAIC_AGENT_COMPACTION_RESERVE_TOKENS=0
# OPENMAIC_AGENT_COMPACTION_KEEP_RECENT_TOKENS=0
# Session prompts and follow-up messages are capped server-side at a fixed
# 100,000 characters; this limit is a constant and is not configurable.
# --- Generation runs ----------------------------------------------------------
# Course generation runs on the server: a run is a PostgreSQL record that a
# worker in every server process executes, step by step, and another process
# takes over from its last checkpoint if one dies. The worker always runs (it
# does not depend on OPENMAIC_AGENT_RUNTIME_ENABLED) and needs a process that
# outlives requests, so serverless hosts are not supported. It shares the lease
# timing of the agent runtime (OPENMAIC_AGENT_RUNTIME_SCAN_INTERVAL_MS,
# _HEARTBEAT_MS, _LEASE_TTL_MS and _MAX_ATTEMPTS above). Defaults are shown;
# values must be positive integers.
#
# Runs one owner may have in progress at once (a run waiting for its outline to
# be confirmed, or paused at a failed step, does not count). Over the limit,
# starting, confirming or retrying a run answers 429 ACTIVE_RUN_LIMIT.
# OPENMAIC_MAX_ACTIVE_RUNS_PER_OWNER=2
# Runs one owner may have waiting for outline confirmation at once.
# OPENMAIC_MAX_WAITING_RUNS_PER_OWNER=10
# Runs one server process executes at once.
# OPENMAIC_GENERATION_RUN_MAX_CONCURRENT=4
# Uploaded materials one server process extracts at once in the background
# (each is parsed or transcribed right after its upload, before generation).
# OPENMAIC_MATERIAL_EXTRACTION_CONCURRENCY=2
# One owner's extractions running at once across every server process, so one
# owner's uploads cannot hold every worker while others wait.
# OPENMAIC_MATERIAL_EXTRACTION_PER_OWNER=2
# The largest stored extraction result (text and images) of one material, in
# MB; a larger one fails with EXTRACTION_RESULT_TOO_LARGE. Results count
# against the owner's material byte quota.
# OPENMAIC_MATERIAL_EXTRACTION_MAX_RESULT_MB=100
# Hours an uploaded material that no generation run or agent session uses is
# kept after it was last read (an open composer reads its materials back) before
# it is deleted.
# OPENMAIC_UNUSED_MATERIAL_TTL_HOURS=24
# Run event streams (one run's events, or the owner's run changes) one owner may
# hold open on one process.
# OPENMAIC_GENERATION_RUN_STREAMS_PER_OWNER=16
# Hours a finished run keeps its full event log before it is compacted to its
# final snapshot.
# OPENMAIC_GENERATION_RUN_RETENTION_HOURS=24
# --- Proxy (optional) --------------------------------------------------------
# HTTP_PROXY=
# HTTPS_PROXY=
# Comma-separated hosts that bypass the proxy. Supports domain suffixes and *.
# NO_PROXY=localhost,127.0.0.1,.internal.example.com
# --- Misc ---------------------------------------------------------------------
# Default model (deprecated: prefer openmaic.yml). Without openmaic.yml it
# becomes the default of every chat slot (the `llm` root), below the models a
# workspace picks in the settings; the agent stays off. There is no hardcoded
# vendor fallback: a call with no model configured fails.
# Example: openai:gpt-5.5, minimax:MiniMax-M2.7-highspeed,
# bedrock:us.anthropic.claude-sonnet-5
DEFAULT_MODEL=
# Per-stage models are slots in openmaic.yml (course.outline,
# course.content.slide, classroom, agent, ...). MODEL_ROUTES is no longer read:
# a server that still sets it without openmaic.yml refuses to start. The agent
# runtime's former "maic-agent-driver" route is now the `agent` slot (with its
# `api` and `contextWindow`).
# Retry model (deprecated: prefer a slot's `fallback` in openmaic.yml). When a
# generation call fails with a retryable failure (SDK-classified transient
# error, timeout, network error, quota 429, capacity 503) or returns empty
# output, the shared callLLM layer retries ONCE on the slot's fallback model.
# Content-safety rejections and other 4xx failures never fall back. Without
# openmaic.yml, MODEL_FALLBACK becomes the fallback of DEFAULT_MODEL.
# MODEL_FALLBACK=
# LOG_LEVEL=info
# LOG_FORMAT=pretty
# LLM_THINKING_DISABLED=false
# Opt-in parallel scene-content generation (#572). 0/unset = serial (default).
# A value > 1 fetches scene content concurrently (capped at 10); actions + TTS
# stay serial. Leave off if your API key has a low per-key concurrency quota.
# PARALLEL_SCENE_CONCURRENCY=3
# --- Local/Self-hosted Deployment ---------------------------------------------
# Set to "true" to allow private/local network URLs. That covers private
# (RFC1918), loopback, link-local and CGNAT (100.64.0.0/10, used by Tailscale
# and similar overlay networks) targets. Required for self-hosted models like
# Ollama. Do NOT enable on public deployments.
# The known cloud instance-metadata and credential endpoints (169.254.169.254,
# 169.254.170.2, 169.254.170.23, 100.100.100.200, 168.63.129.16, 192.0.0.192,
# fd00:ec2::254, fd00:ec2::23, metadata.google.internal) are blocked with or
# without this flag, as are IANA reserved, multicast and broadcast ranges. That
# is a fixed address list, not a general link-local block, and under the flag a
# hostname whose DNS lookup fails, times out or returns no answer is still
# allowed through.
# ALLOW_LOCAL_NETWORKS=true
# Build-time, space-separated CSP frame-ancestor sources in addition to 'self'.
# Configure only origins you trust to embed OpenMAIC, then rebuild the app. The
# Docker and Compose builds also accept this value as a build argument.
# ALLOWED_FRAME_ANCESTORS=https://partner.example.com
# Optional MP4 render service (issue #866). When set, the in-app "Export Video"
# menu offers one-click MP4 rendering; when unset, it degrades to downloading a
# project ZIP for local CLI rendering. Point this at the isolated render-service
# container (see render-service/ and the "video-export" docker-compose profile).
# This is operator-supplied trusted config: the app forwards uploads to it
# without the SSRF guard, so a private/compose-network target works WITHOUT
# setting ALLOW_LOCAL_NETWORKS.
# RENDER_SERVICE_URL=http://render-service:9000
# Render-service-only opt-in for bounded local chunk execution. These variables
# are read at runtime by the isolated render-service container; the public HTTP
# API is unchanged. Defaults keep the existing in-process renderer.
# RENDER_CHUNK_EXECUTION=false
# RENDER_CHUNK_COUNT=1
# RENDER_CHUNK_WORKERS=1
# RENDER_MAX_PARALLEL_CHUNKS=1
# RENDER_CHUNK_SIZE_FRAMES=0
# RENDER_TARGET_CHUNK_FRAMES=0
# Honor x-forwarded-for / x-real-ip when deriving client identity for both
# render-service admission and access-code verification throttling.
# Enable only behind a trusted reverse proxy that overwrites these headers.
# TRUST_PROXY_HEADERS=true
# --- Persistence (PostgreSQL, required) ---------------------------------------
# Courses, chat history, learner progress and generated media are stored in
# PostgreSQL through the embedded /api/persistence endpoint. The server refuses
# to start without DATABASE_URL. Requests are attributed to the owner the owner
# identity seam resolves (by default the anonymous owner cookie); there is no
# separate persistence credential.
#
# Local development: `pnpm db:up` starts a separate development PostgreSQL
# (its own Compose project, container and volume, see docker-compose.db.yml)
# on 127.0.0.1 (port OPENMAIC_DB_PORT, default 5432); then uncomment this line.
# `pnpm db:down` stops it again (the data volume is kept).
# DATABASE_URL=postgres://openmaic:openmaic-dev@127.0.0.1:5432/openmaic
#
# Under docker-compose.yml, the default points at the bundled PostgreSQL (from
# PERSISTENCE_POSTGRES_PASSWORD, which must then be letters and digits); a value
# set here overrides it. Other deployments (the Docker image on its own, or
# `pnpm start`) point this at a PostgreSQL database they run themselves.
# Logical bytes of live assets each owner may hold (default 10 GiB; 0 opts
# out). Per owner, not per deployment: with anonymous-cookie owners a cleared
# cookie is a new owner with a fresh quota.
# ASSET_QUOTA_BYTES=
# The anonymous owner cookie and the ACCESS_CODE cookie carry the `Secure` flag
# in production builds. Over plain HTTP other than localhost no browser stores
# them, so every request is a new owner and the access code is asked again.
# Safari refuses to store `Secure` cookies served over plain http://localhost
# (unlike Chromium/Firefox, it does not special-case localhost), so every
# request mints a fresh anonymous owner and owner-scoped document writes fail
# with 403. Deployments that serve plain HTTP (no TLS) opt out with the exact
# value 0 — any other spelling (false, no) leaves `Secure` on:
# COOKIE_SECURE=0
# Only do this on a trusted network or locally: without `Secure` the cookie
# travels in the clear and can be replayed by anyone on-path.
# Single-tenant deployments: resolve every request to this fixed owner id
# instead of a per-browser anonymous cookie. One team behind one ACCESS_CODE
# then shares one course library — a second browser no longer sees an empty
# list, and `publish` becomes usable (it refuses anonymous owners, since
# publishing a cookie partition is not something the product allows).
# Unset, every browser keeps its own partition and this block changes nothing.
#
# This answers WHO owns a course; DATABASE_URL above answers WHERE one lives.
# Without this setting (or OWNER_SINGLE_USER), a second browser is a different
# anonymous owner and sees an empty list.
#
# ACCESS_CODE is required: without one the middleware lets every request
# through, so a single shared owner would expose one readable, editable and
# publishable course library to anyone who can reach the deployment. Setting
# this without an access code fails startup. Everyone holding the access code
# then sees, edits and can publish the same courses, with no per-person
# attribution — which is what SECURITY.md already says ACCESS_CODE is, and not
# user authentication. 1-128 characters of [A-Za-z0-9._-]; the reserved `anon:`
# prefix is rejected, and a malformed value fails startup rather than being
# ignored.
# PERSISTENCE_SHARED_OWNER_ID=
# Personal installations: resolve every request to one fixed owner for a
# single person, who may publish. On by default under Docker Compose (see
# docker-compose.defaults.env); unset elsewhere. "true"/"1" or "false"/"0";
# anything else fails startup. Excludes PERSISTENCE_SHARED_OWNER_ID above:
# setting both fails startup.
# OWNER_SINGLE_USER=true
#
# The owner id (default "local"), same format as PERSISTENCE_SHARED_OWNER_ID.
# Setting it while OWNER_SINGLE_USER is off fails startup.
# OWNER_SINGLE_USER_ID=local
#
# Exposure: every request becomes the owner of the whole library. The mode runs
# with or without ACCESS_CODE; without one, anyone who can reach the server
# shares, edits and can delete the single library, so the server logs a
# prominent startup warning. Keep it on 127.0.0.1 or a private network
# (docker-compose.yml publishes on 127.0.0.1 by default; OPENMAIC_PUBLISH_ADDRESS
# changes that), or set ACCESS_CODE below.
#
# A browser that used the deployment anonymously before keeps its anonymous
# cookie; single-user mode can claim that earlier work into the single owner
# with POST /api/identity/claim. OWNER_CLAIM_TRIGGER=auto below would claim on
# every visiting browser's first request, irreversibly merging the libraries of
# everyone who used the deployment anonymously; set it only knowingly.
# The owner id is permanent: changing OWNER_SINGLE_USER_ID (or switching from
# PERSISTENCE_SHARED_OWNER_ID) strands the previous owner's library.
# Real accounts (your own sessions, API keys, or an identity gateway such as
# oauth2-proxy or Cloudflare Access) are not configured here: a host registers
# owner auth methods in instrumentation.ts. See "Owner identity" in README.md,
# which includes a recipe for verifying a gateway-signed JWT. When a host
# registers methods and still wants the shared owner above, it includes
# sharedTeamAuthMethod() last; setting PERSISTENCE_SHARED_OWNER_ID beside a
# registration that leaves it out fails startup.
# Store asset bytes in S3 instead of PostgreSQL. Region, endpoint, and credentials
# are resolved through the standard AWS SDK environment / credential chain.
# ASSET_S3_BUCKET=
# Opt into indirect asset byte egress: answer asset byte GETs with a short-lived
# signed S3 URL (a 302, or a JSON descriptor for the packaged client) instead of
# the bytes. Unset or "direct" keeps direct egress (the safe default). Requires
# the object store's CORS to admit this app's origin and expose Content-Type, and
# the signing identity to hold s3:ListBucket on the bucket so a missing key
# answers 404 NoSuchKey rather than 403.
# ASSET_BYTE_EGRESS=redirect
# Claiming anonymous work on sign-in (see README "Claiming anonymous work").
# explicit (default): the app calls POST /api/identity/claim; auto: the first
# request that carries both an account and an anonymous owner claims it.
# OWNER_CLAIM_TRIGGER=explicit
# The page response of a browser's first load mints the anonymous owner cookie
# (default on), so the page's first API requests do not each mint an owner.
# Set it to false only when registered owner auth methods set
# anonymousFallback: false (startup warns when they do and this is on); hosts
# that keep anonymous visitors need it, and skip it per request in
# middleware.ts for requests their methods authenticate.
# "true", "1", "false" or "0"; anything else fails startup.
# OWNER_ANONYMOUS_PREMINT=true
# How long an owner-scoped write (default 30000) and a claim (default 5000) wait
# for the owner's identity lock before answering 503 OWNER_BUSY with
# Retry-After. Positive integers in milliseconds, checked at startup.
# OWNER_WRITE_LOCK_WAIT_MS=30000
# OWNER_CLAIM_LOCK_WAIT_MS=5000
# The asset collector is enabled by default.
# ASSET_COLLECTION_ENABLED=true
# ASSET_COLLECTION_INTERVAL_MS=900000
# ASSET_COLLECTION_GRACE_MS=3600000
# Directory of the file-backed classroom store of earlier versions. Defaults to
# <cwd>/data/classrooms. Classrooms generated through /api/generate-classroom
# are now saved in PostgreSQL (courses) and the asset pool (media, narration);
# at startup the server imports the <id>.json classrooms it finds here, once,
# retrying in the background until it completes, and logs a summary. They go to
# the shared team owner or the single user when one is configured; otherwise to
# a dedicated owner no visitor resolves to, so each classroom keeps opening
# read-only from its old link and appears in no visitor's library (export it as
# a ZIP and import it to own a copy). An id another course already holds is
# skipped. Keep this directory in place: the import leaves the files where they
# are, imported classrooms may still reference files in it, and agent-run
# courses serve narration and material media from it. The old
# <cwd>/data/classroom-jobs directory is no longer read.
# OPENMAIC_CLASSROOMS_DIR=/var/lib/openmaic/classrooms
# How long an allocated asset stays pending -- stored, but not yet named by any
# document -- before the collector expires it. A client stores bytes first and
# writes the id into the document afterwards, so this window has to outlive a
# whole generation pass plus a write-back that is waiting for its slide to be
# built; the default is one day for that reason. A value that is not a positive
# integer stops the server from starting.
# ASSET_PENDING_TTL_MS=86400000
# --- Access Control -----------------------------------------------------------
# Set a password to restrict site access. When set, users must enter this code
# before using the app. Leave empty or remove to disable access control
# (fail-open: middleware lets every request through with no credential).
# Read at runtime. When unset, the server logs a one-time startup warning (in
# single-user mode, the single-user warning, which names ACCESS_CODE, instead);
# GET /api/health reports accessCodeConfigured: false. Set this before exposing
# the server to a network. The warning does not prevent local zero-config use.
# Use a long random value (at least 16 characters from a random generator):
# this code is the only secret guarding the deployment. The code is remembered
# in a signed token stored in an HTTP-only cookie for 7 days; the lifetime is
# enforced server-side, so visitors re-verify after it expires.
# ACCESS_CODE=your-secret-code
#
# Verification is rate limited only when TRUST_PROXY_HEADERS=true (see above):
# behind a trusted reverse proxy that overwrites x-forwarded-for / x-real-ip,
# each client is limited to 10 attempts per 60 seconds, and a trusted client's
# successful verification clears its own counter. Without a trusted proxy the
# app cannot attribute a request to a client, so there is no throttle; rely on
# the length and randomness of the code instead.