Reference: Configuration Schemas
Beach's configuration files are described by JSON Schemas published alongside the package. The schemas are the contract; TypeScript types are a projection. Other languages — when Beach grows bindings beyond TypeScript — validate against the same schema files.
The schemas ship under @cool-ai/beach-config/schemas/ and are
addressed by canonical $id URLs of the form
https://cool-ai.org/protocol/v1/config/<file>.schema.json.
File shapes
Eight schemas, one per file shape:
| Schema | Describes | $id |
|---|---|---|
umbrella.schema.json |
beach.yaml |
https://cool-ai.org/protocol/v1/config/umbrella.schema.json |
functional.schema.json |
beach.functional.yaml |
https://cool-ai.org/protocol/v1/config/functional.schema.json |
environmental.schema.json |
beach.environment.yaml |
https://cool-ai.org/protocol/v1/config/environmental.schema.json |
deployment.schema.json |
beach.environment.<env>.yaml overlays |
https://cool-ai.org/protocol/v1/config/deployment.schema.json |
actor.schema.json |
actors/<id>.yaml (polymorphic on kind) |
https://cool-ai.org/protocol/v1/config/actor.schema.json |
channel.schema.json |
channels/<id>.yaml |
https://cool-ai.org/protocol/v1/config/channel.schema.json |
tool.schema.json |
tools/<id>.yaml |
https://cool-ai.org/protocol/v1/config/tool.schema.json |
model.schema.json |
models/<id>.yaml |
https://cool-ai.org/protocol/v1/config/model.schema.json |
secrets-env.schema.json |
beach.secrets.env |
https://cool-ai.org/protocol/v1/config/secrets-env.schema.json |
common.schema.json |
shared $defs referenced by the others |
https://cool-ai.org/protocol/v1/config/common.schema.json |
The common.schema.json defines seven reusable types referenced by
the others: configVersion (the version: 1 field every file
declares), appExtension (the consumer-defined app: namespace),
appCustomCollection (the shape Beach recommends for a single
consumer-defined collection block under app.collections.<name>),
appExtensionWithCollections (the extended app: block on the
functional file, accepting both arbitrary consumer fields and the
reserved collections namespace), varReference (the ${VAR}
reference pattern), stringOrVarReference, and includeBlock (the
my.cnf-style include vocabulary).
Application-side extensibility
Beach's schemas validate Beach's own fields. Consumer-defined fields
live under app: namespaces and are validated separately, against a
JSON Schema the consumer ships with the application:
- Top-level
app:on the functional file — application-wide consumer settings. - Per-instance
app:on actor / channel / tool files — settings scoped to one thing. app.collections.<name>on the functional file — consumer- defined collections using the same include vocabulary as Beach's own collections. The loader walks the directories and surfaces the assembled instances atconfig.functional.app.collections.<name>.instances.
Recommendation: ship a JSON Schema with every Beach application that
has any consumer-defined app: content, and run the validator with
--with-app-schema <path>. Why: it brings consumer fields under the
same per-file checking Beach gives its own fields, instead of every
adopter writing a parallel validator. The schema may target either
the functional file's beach.app block, per-thing app: blocks, or
both — Beach walks every app: block it finds and validates each
against the schema.
YAML or JSON
Every per-thing schema accepts files written in either YAML or JSON. Recommendation: hand-edited config files use YAML (comments, friendly whitespace); machine-edited config files (typically files an admin UI in the application reads, modifies, and writes back) use JSON (canonical serialiser, no comments to preserve). Why both: forcing a single format penalises one audience for the convenience of the other. The validator checks contents, not extension.
The actor schema is polymorphic
actor.schema.json is a oneOf over three shapes, discriminated by
kind:
kind: llm— the LLM-actor case. Requiresmodel; permitssystemPromptInline | systemPromptFile,tools,domainDataSchema,maxTokens,temperature,inject.kind: handler— the deterministic-handler case. RequireshandlerName. Runtime support arrives with CR-02 (non-LLM participants); v1 accepts the declaration so that no schema migration is required when CR-02 lands.kind: long-running— the durable-workflow case. RequiresruntimeandworkflowId. Same forward-compatibility posture.
A file with kind: llm that omits model is rejected. A file with
kind: handler that adds model is rejected. The validator's error
points at the specific field.
Tool scope is required and prominent
tool.schema.json makes scope a required field with no default.
Router-scope tools form the public capability surface that other peers
can see; specialist-scope tools are internal substrate. A tool file
that omits scope is rejected at validation time, not silently
defaulted.
beach-config tree groups tool files by scope so the public surface
is legible at a glance.
The deployment overlay is a strict allowlist
deployment.schema.json is not a permissive subset of the
environmental schema — it is a strict allowlist of the fields that
legitimately differ between environments: infrastructure.redis.url,
infrastructure.manifestRegistry.backend, infrastructure.missiveStore.backend,
per-channel imap / smtp / pollIntervalSeconds /
manifestTimeoutSeconds, and observability.logLevel /
observability.otlpEndpoint.
Anything not on the allowlist — including any functional field — is rejected. The principle is non-negotiable: an environmental overlay must not reach into interior behaviour. A change to the orchestrator's prompt or to a tool's input schema is a code change, not a deployment change.
Cross-axis enforcement (validator-only)
Two enforcement rules are declared in the schemas' descriptions but applied at validation time, because JSON Schema cannot express them directly:
${VAR}references in functional files. Permitted in environmental and deployment files; rejected anywhere on the functional axis (the functional file itself, every actor file, every tool file, every model file). The validator reports the path and points atbeach.environment.yamlas the correct location.- Secrets-shape rules. A value in
beach.secrets.envthat looks like a plain URL, hostname, port number, model name, or boolean is rejected with a hint pointing atbeach.environment.yaml. A connection string with embedded credentials is permitted but flagged as a warning.
Both rules are part of the v1 contract; future versions can refine the detection rules but cannot remove the enforcement.
Versioning
Every Beach config file declares version: 1 at the top of its
beach: block. The schemas share this version; a future version: 2
introduces a migration path through beach-config migrate (out of v1
scope).
The $id URLs are stable. Schema changes that are non-breaking (added
optional fields, added enum values) ship as patch updates of the
package; schema changes that break existing files would bump the
version field and ship a new $id family at https://cool-ai.org/protocol/v2/....
Related
- Configuring a Beach application — the prose guide.
- Reference: beach-config CLI — the inspection commands.
- Reference: design principles — principle 3.2 (declarative configuration).