# Configuration Reference ## Config Discovery And Precedence Application settings are loaded in this order: 1. Built-in defaults 2. `config.yml` values 3. CLI overrides When `--config` is not provided, Scriptorium searches for config files in this order: 1. `/usr/local/etc/scriptorium/config.yml` 2. `/etc/scriptorium/config.yml` If neither file exists, Scriptorium continues with built-in defaults. When `--config ` is provided, that file is required. ## Minimal App Config ```yaml prompt_dir: ./examples/prompts ``` This is enough to use `run` and `render` when prompts select built-in profiles. ## Production-Oriented App Config ```yaml prompt_dir: /opt/scriptorium/prompts profile_dir: /opt/scriptorium/profiles schema_dir: /opt/scriptorium/schemas server: addr: 127.0.0.1:8080 artifact_root: /var/lib/scriptorium/artifacts max_request_bytes: 16777216 max_artifact_bytes: 16777216 max_response_bytes: 16777216 defaults: render_format: text ``` ## App Config File (`config.yml`) Top-level fields: - `prompt_dir` (optional): default prompt definition directory. - `profile_dir` (optional): default custom profile definition directory. - `schema_dir` (optional): base directory for schema files used by `json_schema` validation. - `server.addr` (optional): default listen address for `serve`. - `server.artifact_root` (optional): base directory for HTTP `file` input references. - `server.max_request_bytes` (optional): maximum HTTP request body size. `0` disables this limit. - `server.max_artifact_bytes` (optional): maximum HTTP `file` input artifact size. `0` disables this limit. - `server.max_response_bytes` (optional): maximum encoded HTTP response body size. `0` disables this limit. - `defaults.render_format` (optional): default `render` output format (`text` or `json`). Built-in defaults: - `schema_dir`: `.` - `server.addr`: `:8080` - `server.artifact_root`: unset; HTTP `file` input references are rejected until configured. - `server.max_request_bytes`: `16777216` (16 MiB) - `server.max_artifact_bytes`: `16777216` (16 MiB) - `server.max_response_bytes`: `16777216` (16 MiB) - `defaults.render_format`: `text` Validation behavior: - Config decoding is strict; unknown YAML fields are rejected. - HTTP size limit values must be greater than or equal to `0`. - Raw API key fields are not supported in `config.yml`. HTTP artifact root behavior: - `server.artifact_root` applies only to `serve`. - HTTP `inline` input references work without an artifact root. - HTTP `file` input references are resolved against `server.artifact_root` and must stay inside it. - Relative traversal and absolute paths outside the root are rejected. - Symlinks inside the root are followed by the operating system; do not make the artifact root writable by untrusted users. - CLI `run` and `render` file inputs keep their normal direct filesystem path behavior. HTTP size-limit behavior: - The request limit covers the encoded JSON request body, including inline input bodies. - The artifact limit covers HTTP `file` input artifacts read through `serve`. - The response limit covers the final encoded JSON response, including generated artifact bodies and `raw_model_output` when requested. - Limits apply only to HTTP `serve`; CLI `run` and `render` keep direct filesystem behavior. ## Prompt Definition Files Prompt definitions are YAML files anywhere under `prompt_dir`, including nested subdirectories. Subdirectories are organizational only. Callers still select prompts by the YAML `id`, not by file path. For example, `prompts/dnd/recap.yaml` may still declare `id: dnd.recap`, and callers use `--prompt dnd.recap`. Example: ```yaml id: generic.structured_events version: "1.0.0" default_profile: local-quality description: Produce structured event JSON from a transcript. inputs: - name: transcript required: true content_type: text/markdown description: Source transcript content - name: glossary required: false content_type: text/yaml description: Optional glossary context messages: - role: system content_file: ./generic.structured_events.system.md - role: user content_file: ./generic.structured_events.user.md output: format: json validation_mode: json_schema schema_path: structured_events.schema.json repair_attempts: 0 ``` Field reference: - `id` (required): prompt identifier. - `version` (required): prompt version. - `default_profile` (optional): profile ID used when request does not provide `profile_id`. - `description` (optional): prompt description. - `session_id` (optional): Go-template string for OpenRouter sticky-routing `session_id`; rendered from request vars. - `inputs` (optional list): expected named inputs. - `messages` (required list): prompt message templates. - `output` (required object): output contract. `inputs[]` fields: - `name` (required) - `required` (optional, boolean) - `content_type` (optional metadata) - `description` (optional) `messages[]` fields: - `role` (required) - `content` or `content_file` (exactly one is required) - `cache_control` (optional object): provider prompt-cache metadata for this message Message rules: - Repeated roles are allowed. - `content_file` is resolved relative to the prompt YAML file location. - Nested prompt files keep the same relative `content_file` behavior; `./recap.user.md` next to `dnd/recap.yaml` resolves from `dnd/`. - Prompt decoding is strict; unknown YAML fields are rejected. - Duplicate prompt IDs are invalid. If multiple files declare the requested prompt ID, Scriptorium fails instead of choosing one. `messages[].cache_control` fields: - `type` (required when `cache_control` is present): currently only `ephemeral`. - `ttl` (optional): currently only `1h`; omitted from outbound requests when unset. Example cache-controlled message: ```yaml messages: - role: system content_file: ./stable_context.md cache_control: type: ephemeral ttl: 1h - role: user content: | {{input "transcript"}} ``` Use cache control on stable reusable prompt content. Dynamic per-run inputs before the cache-controlled message change the provider cache key. Example prompt-level session ID: ```yaml session_id: "{{ .session_id }}" ``` When configured, `session_id` is rendered with the same variable context as messages. The rendered value is trimmed, omitted when empty, and rejected if longer than 256 characters. CLI callers pass the value through `--var session_id=`; HTTP callers pass it through `"vars": {"session_id": ""}`. `output` fields: - `format` (required): `text`, `markdown`, or `json`. - `validation_mode` (required): `none`, `basic`, `json`, or `json_schema`. - `schema_path` (required when `validation_mode: json_schema`). - `repair_attempts` (required): integer `>= 0`. Repair behavior boundary: - `repair_attempts` is part of the prompt contract. - CLI and HTTP currently construct the runner without a repairer, so normal `run`/`serve` execution does not perform output repair attempts. ## Profile Definition Files Scriptorium includes built-in execution profiles. Custom execution profiles are YAML files anywhere under `profile_dir`, including nested subdirectories. Subdirectories are organizational only. Callers still select profiles by the YAML `id`, not by file path. For example, `profiles/local/local-quality.yaml` may still declare `id: local-quality`, and callers use `--profile local-quality`. Example: ```yaml id: local-fast endpoint: http://localhost:8000/v1 model: gpt-4o-mini temperature: 0.2 max_tokens: 500 top_p: 1.0 timeout_seconds: 90 api_key_env: SCRIPTORIUM_API_KEY service_tier: priority reasoning_effort: medium extra_params: provider_route: primary provider_options: retry_budget: 2 ``` Field reference: - `id` (required) - `endpoint` (required) - `model` (required) - `temperature` (optional): range `0..2` - `max_tokens` (optional): `>= 0` - `top_p` (optional): range `0..1` - `timeout_seconds` (optional): `>= 0` - `service_tier` (optional): provider-specific request tier such as OpenRouter `flex` or `priority` - `reasoning_effort` (optional): serialized as top-level `reasoning_effort` in outbound chat-completions requests - `api_key_env` (optional) - `extra_params` (optional map): JSON-compatible provider-specific parameters. Values may be strings, numbers, booleans, objects, or arrays. Profile rules: - `profile_dir` is optional. If omitted, only built-in profiles are available. - If `profile_dir` is set, custom profiles from that directory override built-in profiles with the same `id`. - Duplicate IDs within the custom profile directory are invalid. Matching IDs across custom and built-in profiles are valid override behavior. - Profile decoding is strict; unknown YAML fields are rejected. - Raw `api_key` is rejected; use `api_key_env`. - If `api_key_env` is set, that environment variable must be set when preparing/running. - Duplicate profile IDs are invalid. If multiple files declare the requested profile ID, Scriptorium fails instead of choosing one. - `extra_params` keys must not be empty and must not collide with reserved outbound request fields: `model`, `session_id`, `messages`, `temperature`, `max_tokens`, `top_p`, `service_tier`, `reasoning_effort`, or `response_format`. Built-in profile IDs: | Provider | ID | Model | API key env | | --- | --- | --- | --- | | aion-labs | `aion-2` | `aion-labs/aion-2.0` | `OPENROUTER_API_KEY` | | anthropic | `claude-fable-latest` | `~anthropic/claude-fable-latest` | `OPENROUTER_API_KEY` | | anthropic | `claude-haiku-latest` | `~anthropic/claude-haiku-latest` | `OPENROUTER_API_KEY` | | anthropic | `claude-opus-latest` | `~anthropic/claude-opus-latest` | `OPENROUTER_API_KEY` | | anthropic | `claude-sonnet-latest` | `~anthropic/claude-sonnet-latest` | `OPENROUTER_API_KEY` | | deepseek | `deepseek-3-2` | `deepseek/deepseek-v3.2` | `OPENROUTER_API_KEY` | | deepseek | `deepseek-4-pro` | `deepseek/deepseek-v4-pro` | `OPENROUTER_API_KEY` | | google | `gemini-2-flash` | `google/gemini-2.5-flash` | `OPENROUTER_API_KEY` | | google | `gemini-2-flash-lite` | `google/gemini-2.5-flash-lite` | `OPENROUTER_API_KEY` | | google | `gemini-2-pro` | `google/gemini-2.5-pro` | `OPENROUTER_API_KEY` | | google | `gemini-3-flash-lite` | `google/gemini-3.1-flash-lite` | `OPENROUTER_API_KEY` | | google | `gemini-flash-latest` | `~google/gemini-flash-latest` | `OPENROUTER_API_KEY` | | google | `gemini-pro-latest` | `~google/gemini-pro-latest` | `OPENROUTER_API_KEY` | | google | `gemma-4-31b` | `google/gemma-4-31b-it:exacto` | `OPENROUTER_API_KEY` | | minimax | `minimax-m2` | `minimax/minimax-m2.5` | `OPENROUTER_API_KEY` | | minimax | `minimax-m3` | `minimax/minimax-m3` | `OPENROUTER_API_KEY` | | mistral | `mistral-large-2512` | `mistralai/mistral-large-2512` | `OPENROUTER_API_KEY` | | mistral | `mistral-medium-3-5` | `mistralai/mistral-medium-3-5` | `OPENROUTER_API_KEY` | | mistral | `mistral-small-3` | `mistralai/mistral-small-3.2-24b-instruct` | `OPENROUTER_API_KEY` | | mistral | `mistral-small-4` | `mistralai/mistral-small-2603` | `OPENROUTER_API_KEY` | | nvidia | `nemotron-3-ultra` | `nvidia/nemotron-3-ultra-550b-a55b` | `OPENROUTER_API_KEY` | | openai | `gpt-5-mini` | `openai/gpt-5.4-mini` | `OPENROUTER_API_KEY` | | openai | `gpt-5-nano` | `openai/gpt-5.4-nano` | `OPENROUTER_API_KEY` | Current outbound request behavior: - The OpenAI-compatible client currently serializes: `model`, optional `session_id`, `messages`, `temperature`, `max_tokens`, `top_p`, `service_tier`, `reasoning_effort`, optional `response_format` for `json_schema` prompts, and `extra_params`. - `extra_params` are flattened into provider-specific top-level JSON request fields. They are not wrapped in an `extra_params` object on the outbound provider request. - Messages without `cache_control` serialize with string `content`. - Messages with `cache_control` serialize as a single text content-block array containing `cache_control`. ## Schema Behavior Schemas are JSON files, typically in `schema_dir`. Rules: - `output.validation_mode: json_schema` requires `output.schema_path`. - Relative `schema_path` values resolve from `schema_dir`, including explicit nested paths such as `dnd/structured_events.schema.json`. - Absolute `schema_path` values are used directly. - Scriptorium does not recursively search schemas by basename; nested schemas must be referenced by their relative path. - Missing or invalid schema documents cause runtime validation errors. - Invalid generated JSON causes validation status `failed` (not a runtime error). Supported artifact reference types for request inputs are `file` and `inline`. For HTTP `serve`, `file` references require `server.artifact_root` and must stay inside that root. CLI `run` and `render` file inputs are not restricted by `server.artifact_root`. ## Secrets Handling - Keep secret values in environment variables. - Store only environment-variable names in profile `api_key_env`. - Do not put raw API keys in config, prompts, profiles, CLI flags, or HTTP request bodies. ## Maintained Examples - App config: `examples/config.yml` - Prompt examples: `examples/prompts/` - Custom profile examples: `examples/profiles/` - Schema examples: `examples/schemas/` - Input fixtures: `examples/fixtures/` - Render example script: `examples/render-markdown-summary.sh` - HTTP request example: `examples/http-run.json` Example organizational layout: ```text examples/prompts/dnd/recap.yaml examples/profiles/local/local-quality.yaml examples/schemas/dnd/structured_events.schema.json ``` ## Integration References - [Inbound HTTP contract](integrations/http-api.md) - [Outbound OpenAI-compatible contract](integrations/openai-compatible-chat.md)