jacqos.toml Reference
Overview
Section titled “Overview”jacqos.toml is the root manifest for a JacqOS application. It lives at the top of your app directory and declares everything the shell needs to load, evaluate, and execute it: app identity, file globs, effect capabilities, external resources, studio settings, observation retention, and per-mapper resource budgets.
The shell parses jacqos.toml with strict deserialization: unknown keys at the top level and inside almost every nested table are rejected at load time. Typos surface immediately rather than silently disabling a feature.
This page documents every accepted key. The portable JSON Schema lives at spec/jacqos/v1/jacqos-toml.schema.json and is kept aligned with the Rust deserialization in the shell.
Stability Tags
Section titled “Stability Tags”Every documented key carries one of three stability tags, per the JacqOS hardening policy:
contract— frozen surface. Do not change without explicit authorization. Examples:app_id,capabilities.*.pinned— V1 stable. Preserved by default; explicit changes are flagged. Examples:paths.*,studio.*,retention.*.exploratory— subject to change. Use, but expect movement. Exploratory keys are called out explicitly when they appear.
Tags appear in the column labelled “Stability” throughout this reference. The platform-wide commitments behind these tags — what stays stable in V1.x, what changes only at V2, and how app_version interacts with jacqos --version — live in V1 Stability and Upgrade Promises.
Top-Level Keys
Section titled “Top-Level Keys”The manifest accepts exactly 9 top-level keys. Anything else is a load error.
| Field | Type | Required | Default | Stability | Description |
|---|---|---|---|---|---|
app_id | string | Yes | — | contract | Stable application identifier. Must be non-blank. Appears in every export, digest, and persisted artifact. |
app_version | string | Yes | — | contract | Human-facing version string. Must be non-blank. Visible in every export. |
paths | table | No | see [paths] | pinned | Glob patterns for source file discovery. |
capabilities | table | No | see [capabilities] | contract | Effect capability declarations. |
resources | table | No | see [resources] | contract | Concrete configuration for HTTP and model resources. |
studio | table | No | see [studio] | pinned | Studio-only configuration. |
retention | table | No | see [retention] | pinned | Observation retention policy. |
enterprise | table | No | see [enterprise] | contract | Enterprise strict-mode controls. |
mapper_budgets | map of tables | No | {} | pinned | Per-mapper resource limits keyed by mapper file stem. |
Minimal Example
Section titled “Minimal Example”app_id = "my-booking-app"app_version = "0.1.0"That is a valid jacqos.toml. Every other section has sensible defaults.
Worked Example
Section titled “Worked Example”This example exercises every documented key. It is adapted from examples/jacqos-chevy-offer-containment/jacqos.toml so that result_kind, provider_mode, and operator_actions all appear at least once.
# Identity (required).app_id = "appointment-booking"app_version = "0.1.0"
# Source file globs. Each list overrides the per-kind default.[paths]ontology = ["ontology/*.dh"]mappings = ["mappings/*.rhai"]helpers = ["helpers/*.rhai", "helpers/*.wasm"]prompts = ["prompts/*.md"]schemas = ["schemas/*.json"]fixtures = ["fixtures/*.jsonl"]
# Effect capability declarations.[capabilities]http_clients = ["clinic_api", "notify_api"]models = ["intake_triage"]timers = trueblob_store = truedev_log = false # alias `log_dev` is accepted; prefer `dev_log`
# Map every `intent.*` relation to a concrete capability + resource.# Table-of-tables shape (NOT `[[intents]]`).[capabilities.intents]"intent.reserve_slot" = { capability = "http.fetch", resource = "clinic_api" }"intent.send_confirmation" = { capability = "http.fetch", resource = "notify_api" }"intent.extract_intake" = { capability = "llm.complete", resource = "intake_triage", result_kind = "llm.intake_extraction_result" }"intent.set_reminder" = { capability = "timer.schedule" }"intent.store_document" = { capability = "blob.put" }"intent.note_event" = { capability = "log.dev" }
# Concrete HTTP clients.[resources.http.clinic_api]base_url = "https://clinic.example.invalid"credential_ref = "CLINIC_API_TOKEN"replay = "record"allowed_hosts = ["clinic.example.invalid"]tls = "https_only"
[resources.http.notify_api]base_url = "https://notify.example.invalid"credential_ref = "NOTIFY_API_TOKEN"replay = "record"allowed_hosts = ["notify.example.invalid"]tls = "https_only"
# Concrete LLM model.[resources.model.intake_triage]provider = "openai"model = "gpt-4o-mini"base_url = "https://api.openai.com/v1"credential_ref = "OPENAI_API_KEY"schema = "schemas/intake-extraction.json"replay = "record"
# Optional: run the model under the deterministic Rhai sandbox for demos.[resources.model.intake_triage.provider_mode]mode = "deterministic"script = "prompts/intake-deterministic.rhai"
# Studio (UI) configuration.[studio]default_example = "happy-path" # must match a fixture stemoperator_actions = "disabled" # `enabled` for bundled demos only
[studio.display]name = "Appointment Booking"accent_color = "#0f766e"
# Observation retention policy.[retention]archive_after = "90d"
# Enterprise hardening. Default is false for demos and local prototypes.[enterprise]strict = false
# Per-mapper resource limits. Key must match a mapper file stem.[mapper_budgets.inbound]max_operations = 150_000max_string_size = "2MB"max_array_size = 12_000payload_limit = "12MB"timeout = "10s"[paths]
Section titled “[paths]”Glob patterns for source file discovery. Every list is optional. When omitted or empty, the per-kind default applies. All globs must stay within the app root — .. escape is rejected.
| Field | Type | Default | Stability | Description |
|---|---|---|---|---|
ontology | array of strings | ["ontology/*.dh"] | pinned | .dh ontology, schema, and intent rule files. |
mappings | array of strings | ["mappings/*.rhai"] | pinned | Rhai observation mappers. |
helpers | array of strings | ["helpers/*.rhai", "helpers/*.wasm"] | pinned | Pure helper functions (Rhai or pre-compiled Wasm). |
prompts | array of strings | ["prompts/*.md"] | pinned | Prompt markdown files included in the prompt bundle. |
schemas | array of strings | ["schemas/*.json"] | pinned | JSON Schemas for structured LLM outputs. |
fixtures | array of strings | ["fixtures/*.jsonl"] | pinned | Replayable fixture timelines. |
[paths]ontology = ["ontology/*.dh", "ontology/**/*.dh"]mappings = ["mappings/*.rhai"][capabilities]
Section titled “[capabilities]”Declares what your application is allowed to do. Undeclared capability use is a hard load error — the shell rejects it before any effects execute.
| Field | Type | Default | Stability | Description |
|---|---|---|---|---|
http_clients | array of strings | [] | contract | Names of HTTP resources the app may use. Each name must have a matching [resources.http.<name>]. |
models | array of strings | [] | contract | Names of LLM model resources the app may use. Each name must have a matching [resources.model.<name>]. |
timers | bool | false | contract | Permits timer.schedule effects. |
blob_store | bool | false | contract | Permits blob.put and blob.get effects. |
dev_log | bool | false | contract | Permits log.dev effects. |
log_dev | bool | (alias) | pinned | Compatibility alias for dev_log. Accepted at parse time; prefer dev_log. |
intents | map | {} | contract | Maps intent.* relation names to the capability that satisfies them. See [capabilities.intents]. |
[capabilities.intents]
Section titled “[capabilities.intents]”Maps every intent.* relation derived by your ontology to a concrete capability binding. The shell uses these bindings to route intents to effect handlers. Every intent the ontology can derive must have a binding here, or the intent will fail to dispatch at runtime.
Use
[capabilities.intents], not[[intents]]. The canonical shape is a table-of-tables keyed by quoted relation names. The[[intents]]table-array form (withrelationandendpointfields) is not accepted by the loader and will fail with an “unknown field” error at the top level. If you see[[intents]]in older notes, replace it with the form documented below.
[capabilities.intents]"intent.reserve_slot" = { capability = "http.fetch", resource = "clinic_api" }"intent.extract_intake" = { capability = "llm.complete", resource = "intake_triage", result_kind = "llm.intake_extraction_result" }"intent.set_reminder" = { capability = "timer.schedule" }"intent.store_document" = { capability = "blob.put" }"intent.note_event" = { capability = "log.dev" }You can also write each binding as its own sub-table when you prefer one block per intent:
[capabilities.intents."intent.extract_intake"]capability = "llm.complete"resource = "intake_triage"result_kind = "llm.intake_extraction_result"The map key must start with intent. and is validated at load time. Each binding accepts:
| Field | Type | Required | Stability | Description |
|---|---|---|---|---|
capability | enum | Yes | contract | One of the effect capabilities below. |
resource | string | For http.fetch and llm.complete | contract | Name of the HTTP or model resource to use. Forbidden for blob.*, timer.schedule, and log.dev. |
result_kind | string | For llm.complete | contract | Observation kind appended on successful structured model output. Forbidden for every other capability. |
Effect Capabilities
Section titled “Effect Capabilities”| Capability | Purpose | Requires resource | Requires result_kind |
|---|---|---|---|
http.fetch | Declared outbound HTTP | Yes | No |
llm.complete | Explicit model call | Yes | Yes |
blob.put | Store large payload | No | No |
blob.get | Retrieve large payload | No | No |
timer.schedule | Request a future timer observation | No | No |
log.dev | Developer diagnostics (never canonical state) | No | No |
[resources]
Section titled “[resources]”Concrete configuration for HTTP clients and LLM models declared in [capabilities].
| Field | Type | Default | Stability | Description |
|---|---|---|---|---|
http | map of tables | {} | contract | HTTP client configuration keyed by client name. |
model | map of tables | {} | contract | LLM model configuration keyed by model name. |
Every name listed in capabilities.http_clients must have a matching [resources.http.<name>] section, and vice versa. The same rule applies to capabilities.models and [resources.model.<name>]. Mismatches are caught at load time.
[resources.http.<name>]
Section titled “[resources.http.<name>]”[resources.http.clinic_api]base_url = "https://clinic.example.invalid"credential_ref = "CLINIC_API_TOKEN"replay = "record"allowed_hosts = ["clinic.example.invalid"]tls = "https_only"| Field | Type | Required | Default | Stability | Description |
|---|---|---|---|---|---|
base_url | string | No | — | pinned | Base URL for the HTTP service. Must start with http:// or https:// if present. |
credential_ref | string | No | — | contract | Name of an environment variable holding the secret. Token-like identifiers only — literal Bearer … or sk-… values are rejected. |
replay | enum: record / replay | No | — | contract | Provider capture policy. record allows live dispatch and persists the redacted capture; replay refuses live dispatch unless a matching capture already exists. |
allowed_hosts | array of strings | No | [] | contract | Explicit egress allow-list. If omitted, JacqOS allows only the base_url host. Entries are exact hosts, IP literals, or wildcard suffixes like *.example.com. |
allow_private_network | boolean | No | false | contract | Permit private or local network destinations for this resource. Metadata endpoints are always denied. |
tls | enum: https_only / allow_http | No | https_only | contract | HTTPS policy for live dispatch. Use allow_http only for local development or an explicitly trusted plain-HTTP endpoint. |
Credentials are never stored in jacqos.toml. The credential_ref field names an environment variable; the shell reads the value at runtime. This keeps secrets out of version control and out of evaluation packages.
Live HTTP effects also run through an egress boundary before network dispatch. JacqOS checks the allow-list, resolves and pins the destination addresses, blocks metadata endpoints, blocks private/local networks unless explicitly enabled, disables environment proxies, and does not follow redirects. Successful captures record egress audit metadata alongside the redacted request and response.
[resources.model.<name>]
Section titled “[resources.model.<name>]”[resources.model.intake_triage]provider = "openai"model = "gpt-4o-mini"base_url = "https://api.openai.com/v1"credential_ref = "OPENAI_API_KEY"schema = "schemas/intake-extraction.json"replay = "record"| Field | Type | Required | Default | Stability | Description |
|---|---|---|---|---|---|
provider | string | No | — | pinned | Free-form provider identifier (e.g. "openai", "anthropic"). |
model | string | Yes | — | contract | Concrete provider model id (e.g. "gpt-4o-mini"). Must be non-blank when this section is present. |
base_url | string | No | — | pinned | Optional provider URL override, mainly useful for proxies or local testing. Must start with http:// or https:// if present. |
credential_ref | string | No | — | contract | Environment variable name for the API key. Same secret-form rules as HTTP. |
schema | string | No | — | contract | Path to a JSON schema for structured output. Must resolve under [paths].schemas. |
replay | enum: record / replay | No | — | contract | Provider capture policy. record allows provider dispatch and persists the capture; replay refuses provider dispatch unless a matching capture already exists. |
provider_mode | table | No | — | contract | Override how the model provider executes. See below. |
[resources.model.<name>.provider_mode]
Section titled “[resources.model.<name>.provider_mode]”Lets you swap the model’s execution behaviour without changing the rest of the manifest. Use deterministic for bundled demos that need to run without an API key, record when you want live provider calls captured for replay, and replay when CI or an operator run must not call the provider.
[resources.model.sales_decision_model.provider_mode]mode = "deterministic"script = "prompts/offer-decision-deterministic.rhai"| Field | Type | Required | Default | Stability | Description |
|---|---|---|---|---|---|
mode | enum: live / deterministic / record / replay | Yes (when section is present) | live | contract | Provider execution mode. record calls the live provider and persists the capture; replay requires an existing capture and never calls the provider. |
script | string | When mode = "deterministic" | — | contract | Path to a Rhai script that produces the deterministic response. Workspace-root relative. |
See LLM Agents for how model resources participate in the intent lifecycle. The normative provider capture rules are pinned in spec/jacqos/v1/provider-record-replay.md.
[studio]
Section titled “[studio]”Configuration for JacqOS Studio.
[studio]default_example = "happy-path"operator_actions = "disabled"
[studio.display]name = "Appointment Booking"accent_color = "#0f766e"| Field | Type | Required | Default | Stability | Description |
|---|---|---|---|---|---|
default_example | string | No | — | pinned | Fixture stem (without .jsonl) Studio loads on first launch. Must match a loaded fixture. |
operator_actions | enum: disabled / enabled | No | disabled | pinned | Whether Studio surfaces operator affordances (Ratify, Reject, Promote, Append correction). Bundled demos opt in; production stays off. |
display | table | No | — | pinned | Branding metadata. See [studio.display]. |
[studio.display]
Section titled “[studio.display]”| Field | Type | Required | Default | Stability | Description |
|---|---|---|---|---|---|
name | string | No | — | pinned | Human-readable display name shown in Studio. |
accent_color | string | No | — | pinned | CSS hex color (#RRGGBB or #RRGGBBAA). Validated. |
[retention]
Section titled “[retention]”Controls observation store lifecycle.
[retention]archive_after = "90d"| Field | Type | Required | Default | Stability | Description |
|---|---|---|---|---|---|
archive_after | string (duration) | No | — | pinned | Window after which observations become eligible for jacqos gc. Format: <int><unit> with unit in s, m, h, d. Must be greater than zero. |
Retention does not rewrite semantic history. The observation log remains the canonical evidence surface; retention controls whether older raw evidence is eligible for archive or GC handling. Enterprise privacy workflows build on the same model with encrypted BlobRefs, legal holds, tombstoned access, and redacted projections that preserve stable identifiers and digests.
[enterprise]
Section titled “[enterprise]”Enterprise strict mode turns high-stakes warnings and ambiguous shortcuts into hard failures. Leave it off while you prototype. Turn it on before you use the app as evidence for an enterprise trust boundary.
[enterprise]strict = true| Field | Type | Required | Default | Stability | Description |
|---|---|---|---|---|---|
strict | boolean | No | false | contract | When true, JacqOS rejects unknown mapper-budget keys, missing or weak mapper budgets, unsafe HTTP egress config, model resources without schema/replay policy, unconstrained rule shapes, incomplete policy/review metadata, and default-allow evidence warnings. |
[mapper_budgets.<mapper>]
Section titled “[mapper_budgets.<mapper>]”Per-mapper resource limits. Each key names a mapper file stem found via [paths].mappings. Protects against malicious or oversized payloads.
[mapper_budgets.inbound]max_operations = 150_000max_string_size = "2MB"max_array_size = 12_000payload_limit = "12MB"timeout = "10s"| Field | Type | Required | Default | Stability | Description |
|---|---|---|---|---|---|
max_operations | integer | No | 100_000 | contract | Maximum Rhai operations per invocation. Must be greater than zero. |
max_string_size | integer or string | No | "1MB" | contract | Maximum single string size. |
max_array_size | integer | No | 10_000 | contract | Maximum array element count. Must be greater than zero. |
payload_limit | integer or string | No | "10MB" | contract | Maximum observation payload size. |
timeout | integer or string | No | "5s" | contract | Wall-clock timeout. Must be greater than zero. |
Compatibility note: outside enterprise strict mode,
[mapper_budgets.<mapper>]preserves the historical behavior where unknown keys are ignored. With[enterprise] strict = true, unknown mapper-budget keys are hard load errors.
Size units
Section titled “Size units”Size fields accept bare integers (interpreted as bytes) or strings with a unit suffix: b, kb, mb, gb, kib, mib, gib. Examples: 1000, "1MB", "512KiB".
Duration units
Section titled “Duration units”Duration fields accept bare integers (interpreted as seconds) or strings with a unit suffix: ms, s, m, h. Examples: 5, "5s", "300ms", "1m".
See Rhai Mapper API — Resource Budgets for what each budget protects against.
Validation
Section titled “Validation”The shell validates jacqos.toml at load time and rejects manifests with:
- Missing or blank
app_idorapp_version. - HTTP client names declared in
capabilities.http_clientswithout a matching[resources.http.<name>](and vice versa). - Model names declared in
capabilities.modelswithout a matching[resources.model.<name>](and vice versa). - Intent bindings whose key does not start with
intent.. http.fetchorllm.completeintent bindings missing aresourcefield.llm.completeintent bindings missing aresult_kindfield.resourceorresult_kindset on a binding that does not allow them (e.g.resourceon atimer.schedulebinding).- Literal secret values placed in
credential_ref(e.g."Bearer …","sk-…"). base_urlvalues that don’t start withhttp://orhttps://.- Invalid size or duration formats.
- Glob patterns that escape the app root.
- Unknown fields anywhere except inside
[mapper_budgets.<mapper>].
Next Steps
Section titled “Next Steps”- CLI Reference — every CLI command
- Evaluation Package — the portable contract boundary
- Rhai Mapper API — mapper contract and resource budgets
- Effects and Intents — how capabilities drive the intent lifecycle