Skip to content

jacqos.toml Reference

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.

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.

The manifest accepts exactly 9 top-level keys. Anything else is a load error.

FieldTypeRequiredDefaultStabilityDescription
app_idstringYescontractStable application identifier. Must be non-blank. Appears in every export, digest, and persisted artifact.
app_versionstringYescontractHuman-facing version string. Must be non-blank. Visible in every export.
pathstableNosee [paths]pinnedGlob patterns for source file discovery.
capabilitiestableNosee [capabilities]contractEffect capability declarations.
resourcestableNosee [resources]contractConcrete configuration for HTTP and model resources.
studiotableNosee [studio]pinnedStudio-only configuration.
retentiontableNosee [retention]pinnedObservation retention policy.
enterprisetableNosee [enterprise]contractEnterprise strict-mode controls.
mapper_budgetsmap of tablesNo{}pinnedPer-mapper resource limits keyed by mapper file stem.
app_id = "my-booking-app"
app_version = "0.1.0"

That is a valid jacqos.toml. Every other section has sensible defaults.

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 = true
blob_store = true
dev_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 stem
operator_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_000
max_string_size = "2MB"
max_array_size = 12_000
payload_limit = "12MB"
timeout = "10s"

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.

FieldTypeDefaultStabilityDescription
ontologyarray of strings["ontology/*.dh"]pinned.dh ontology, schema, and intent rule files.
mappingsarray of strings["mappings/*.rhai"]pinnedRhai observation mappers.
helpersarray of strings["helpers/*.rhai", "helpers/*.wasm"]pinnedPure helper functions (Rhai or pre-compiled Wasm).
promptsarray of strings["prompts/*.md"]pinnedPrompt markdown files included in the prompt bundle.
schemasarray of strings["schemas/*.json"]pinnedJSON Schemas for structured LLM outputs.
fixturesarray of strings["fixtures/*.jsonl"]pinnedReplayable fixture timelines.
[paths]
ontology = ["ontology/*.dh", "ontology/**/*.dh"]
mappings = ["mappings/*.rhai"]

Declares what your application is allowed to do. Undeclared capability use is a hard load error — the shell rejects it before any effects execute.

FieldTypeDefaultStabilityDescription
http_clientsarray of strings[]contractNames of HTTP resources the app may use. Each name must have a matching [resources.http.<name>].
modelsarray of strings[]contractNames of LLM model resources the app may use. Each name must have a matching [resources.model.<name>].
timersboolfalsecontractPermits timer.schedule effects.
blob_storeboolfalsecontractPermits blob.put and blob.get effects.
dev_logboolfalsecontractPermits log.dev effects.
log_devbool(alias)pinnedCompatibility alias for dev_log. Accepted at parse time; prefer dev_log.
intentsmap{}contractMaps intent.* relation names to the capability that satisfies them. See [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 (with relation and endpoint fields) 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:

FieldTypeRequiredStabilityDescription
capabilityenumYescontractOne of the effect capabilities below.
resourcestringFor http.fetch and llm.completecontractName of the HTTP or model resource to use. Forbidden for blob.*, timer.schedule, and log.dev.
result_kindstringFor llm.completecontractObservation kind appended on successful structured model output. Forbidden for every other capability.
CapabilityPurposeRequires resourceRequires result_kind
http.fetchDeclared outbound HTTPYesNo
llm.completeExplicit model callYesYes
blob.putStore large payloadNoNo
blob.getRetrieve large payloadNoNo
timer.scheduleRequest a future timer observationNoNo
log.devDeveloper diagnostics (never canonical state)NoNo

Concrete configuration for HTTP clients and LLM models declared in [capabilities].

FieldTypeDefaultStabilityDescription
httpmap of tables{}contractHTTP client configuration keyed by client name.
modelmap of tables{}contractLLM 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.clinic_api]
base_url = "https://clinic.example.invalid"
credential_ref = "CLINIC_API_TOKEN"
replay = "record"
allowed_hosts = ["clinic.example.invalid"]
tls = "https_only"
FieldTypeRequiredDefaultStabilityDescription
base_urlstringNopinnedBase URL for the HTTP service. Must start with http:// or https:// if present.
credential_refstringNocontractName of an environment variable holding the secret. Token-like identifiers only — literal Bearer … or sk-… values are rejected.
replayenum: record / replayNocontractProvider capture policy. record allows live dispatch and persists the redacted capture; replay refuses live dispatch unless a matching capture already exists.
allowed_hostsarray of stringsNo[]contractExplicit 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_networkbooleanNofalsecontractPermit private or local network destinations for this resource. Metadata endpoints are always denied.
tlsenum: https_only / allow_httpNohttps_onlycontractHTTPS 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.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"
FieldTypeRequiredDefaultStabilityDescription
providerstringNopinnedFree-form provider identifier (e.g. "openai", "anthropic").
modelstringYescontractConcrete provider model id (e.g. "gpt-4o-mini"). Must be non-blank when this section is present.
base_urlstringNopinnedOptional provider URL override, mainly useful for proxies or local testing. Must start with http:// or https:// if present.
credential_refstringNocontractEnvironment variable name for the API key. Same secret-form rules as HTTP.
schemastringNocontractPath to a JSON schema for structured output. Must resolve under [paths].schemas.
replayenum: record / replayNocontractProvider capture policy. record allows provider dispatch and persists the capture; replay refuses provider dispatch unless a matching capture already exists.
provider_modetableNocontractOverride how the model provider executes. See below.

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"
FieldTypeRequiredDefaultStabilityDescription
modeenum: live / deterministic / record / replayYes (when section is present)livecontractProvider execution mode. record calls the live provider and persists the capture; replay requires an existing capture and never calls the provider.
scriptstringWhen mode = "deterministic"contractPath 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.

Configuration for JacqOS Studio.

[studio]
default_example = "happy-path"
operator_actions = "disabled"
[studio.display]
name = "Appointment Booking"
accent_color = "#0f766e"
FieldTypeRequiredDefaultStabilityDescription
default_examplestringNopinnedFixture stem (without .jsonl) Studio loads on first launch. Must match a loaded fixture.
operator_actionsenum: disabled / enabledNodisabledpinnedWhether Studio surfaces operator affordances (Ratify, Reject, Promote, Append correction). Bundled demos opt in; production stays off.
displaytableNopinnedBranding metadata. See [studio.display].
FieldTypeRequiredDefaultStabilityDescription
namestringNopinnedHuman-readable display name shown in Studio.
accent_colorstringNopinnedCSS hex color (#RRGGBB or #RRGGBBAA). Validated.

Controls observation store lifecycle.

[retention]
archive_after = "90d"
FieldTypeRequiredDefaultStabilityDescription
archive_afterstring (duration)NopinnedWindow 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 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
FieldTypeRequiredDefaultStabilityDescription
strictbooleanNofalsecontractWhen 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.

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_000
max_string_size = "2MB"
max_array_size = 12_000
payload_limit = "12MB"
timeout = "10s"
FieldTypeRequiredDefaultStabilityDescription
max_operationsintegerNo100_000contractMaximum Rhai operations per invocation. Must be greater than zero.
max_string_sizeinteger or stringNo"1MB"contractMaximum single string size.
max_array_sizeintegerNo10_000contractMaximum array element count. Must be greater than zero.
payload_limitinteger or stringNo"10MB"contractMaximum observation payload size.
timeoutinteger or stringNo"5s"contractWall-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 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 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.

The shell validates jacqos.toml at load time and rejects manifests with:

  • Missing or blank app_id or app_version.
  • HTTP client names declared in capabilities.http_clients without a matching [resources.http.<name>] (and vice versa).
  • Model names declared in capabilities.models without a matching [resources.model.<name>] (and vice versa).
  • Intent bindings whose key does not start with intent..
  • http.fetch or llm.complete intent bindings missing a resource field.
  • llm.complete intent bindings missing a result_kind field.
  • resource or result_kind set on a binding that does not allow them (e.g. resource on a timer.schedule binding).
  • Literal secret values placed in credential_ref (e.g. "Bearer …", "sk-…").
  • base_url values that don’t start with http:// or https://.
  • Invalid size or duration formats.
  • Glob patterns that escape the app root.
  • Unknown fields anywhere except inside [mapper_budgets.<mapper>].