Skip to content

Configuration

Your entire gateway is one YAML file (or a few, if you split it up). Here’s every top-level key you can set and what it does.

version: "1.0.0"
proxy:
expose:
- name: hello.echo
executor: { kind: noop }

That’s a valid config. Everything except version is optional. You add keys as you need them.

Required, non-empty string.

version: "1.0.0"

"1.0.0" is the current convention. The value isn’t parsed as semver or checked against a fixed list — any non-empty string is accepted — but it gives the gateway a hook to detect and migrate config formats as the schema evolves.

Merge other YAML files into this config before the gateway starts. Maps merge (later wins on key collisions), arrays concatenate.

include:
- ./connections.yaml
- ./workflows/deploy.yaml
- ./workflows/review.yaml

Paths are relative to the main config file. This is useful for splitting a large config across teams or environments — keep shared connections in one file, team-specific workflows in another.

An entry can also be an object { uri, hash } for remote or verified includes:

include:
- ./connections.yaml
- { uri: "file:///etc/flowgate/shared.yaml" }
- { uri: "https://example.com/policy.yaml", hash: "sha256:..." }

Supported schemes are file://, https://, and git+https://. Any non-file:// URI requires a hash (sha256) so the merged config is reproducible and tamper-evident; the gateway refuses to merge if the fetched body doesn’t match.

Pull in versioned resource repos — shared collections of capabilities, orchestrators, skills, and scripts that live in their own directory with a flowgate.repo.yaml manifest at the root. This is the primary composition mechanism: instead of copying definitions between configs, you reference a repo and its definitions are merged into your gateway registry at load time.

repos:
- path: ~/repos/cognitive-architectures
- path: ../flowgate-meta

Each entry is an object with a path field pointing at the repo directory. Relative paths resolve against the host config’s directory (the same convention as include:), and a leading ~/ expands to your home directory.

Every definition loaded from a repo is namespace-prefixed with the repo’s declared namespace — a repo whose manifest declares namespace: cognitive surfaces its ids as cognitive/<id> (e.g. cognitive/flow.add-feature). Two declared repos that share a namespace fail at config load (DUPLICATE_REPO_NAMESPACE).

The repo’s flowgate.repo.yaml manifest must declare schema: flowgate.repo/v1, a name, a namespace, and a version, plus an optional layout mapping the directories where each tier lives (defaults: capabilities/, orchestrators/, skills/, scripts/, connections/).

When a repo provides a definition and your host config defines an id that collides with it, you must explicitly acknowledge the shadowing by listing the fully-qualified id in the top-level overrides: array. This closes a supply-chain backdoor: an operator can’t silently shadow a vendored definition.

overrides:
- cognitive/cap.plan.vet

overrides: is an array of fully-qualified (namespace-prefixed) id strings. With the override declared, your host definition wins the collision. The gateway errors if you shadow a repo-provided id without listing it (ANONYMOUS_OVERRIDE), or if you list an id that no declared repo actually provides (STALE_OVERRIDE).

Named, reusable capability definitions. You define a capability once here, then reference it from proxy.expose or from workflow transition executors.

capabilities:
github.list_issues:
title: List GitHub issues
description: List issues from a GitHub repository.
tags: [github, issues]
inputSchema:
type: object
required: [repo]
properties:
repo: { type: string }
additionalProperties: false
executor:
kind: mcp
connection: github
tool: list_issues
guards:
- { kind: role, role: developer }
reliability:
timeoutMs: 30000
retry:
maxAttempts: 2
backoff: fixed
initialDelayMs: 1000

You can also wrap another capability to add guards or reliability on top:

capabilities:
github.list_issues.safe:
wraps: github.list_issues
guards:
- { kind: permission, permission: github.read }

The wrapper inherits the wrapped capability’s executor and stacks its own guards and reliability on top.

Named MCP server connections. The gateway speaks to these at runtime. Three kinds:

connections:
github:
kind: mcp
command: github-mcp-server
args: []
env:
GITHUB_TOKEN: "${GITHUB_TOKEN}"

The gateway launches this as a child process and speaks MCP over stdio. Works with any MCP server — native binaries, npx shims, uvx launchers, Docker containers.

connections:
filesystem:
kind: mcp
command: npx
args: [-y, "@modelcontextprotocol/server-filesystem", "/tmp"]

If you provide url instead of command, the gateway connects to a running MCP server over the Streamable HTTP transport:

connections:
remote_server:
kind: mcp
url: https://mcp.example.com/mcp

When both url and command are set, url wins — a URL implies a hosted server rather than a process to launch.

connections:
dotnet:
kind: cli
command: dotnet
workingDirectory: /path/to/project
env:
DOTNET_ENV: production

Used with the cli executor. The gateway runs shell commands through this connection.

connections:
payroll:
kind: rest
baseUrl: https://payroll.example.com
headers:
Authorization: "Bearer ${PAYROLL_TOKEN}"

Used with the rest executor. Headers defined here apply to every request through this connection.

The proxy section defines what capabilities the gateway exposes to the model. Two mechanisms: expose (define inline or reference capabilities) and import (auto-import from MCP connections).

Each entry is either an inline definition or a reference to a named capability.

Inline definition — you define everything right here:

proxy:
expose:
- name: github.list_issues
title: List GitHub issues
description: List issues from a GitHub repository.
tags: [github, issues, read]
aliases: [gh-issues, list-issues]
examples:
- "List all open issues in the main repo"
inputSchema:
type: object
required: [repo]
properties:
repo: { type: string }
additionalProperties: false
executor:
kind: mcp
connection: github
tool: list_issues
guards:
- { kind: role, role: developer }
reliability:
timeoutMs: 30000

Capability reference — point to a named capability:

proxy:
expose:
- capability: github.list_issues
as: issues.list # optional: rename for the proxy
description: Override description if you want
tags: [extra-tag] # merged with the capability's tags
aliases: [find-issues] # search synonyms
guards: # stacked on top of the capability's guards
- { kind: permission, permission: repo.read }

Auto-import tools from a connected MCP server. The gateway calls tools/list on the connection at startup and creates a proxy exposure for each tool.

proxy:
import:
- connection: github
prefix: github # tools become github.list_issues, github.create_issue, etc.
include: [list_issues, create_issue] # only these tools (omit for all)
exclude: [delete_repo] # skip these tools
tags: [github, source-control] # applied to all imported tools

You can mix imports with explicit declarations. Declared capabilities can carry guards and reliability that imports don’t have by default.

Multi-state workflow definitions. Each workflow is a state machine with states, transitions, guards, and executors.

workflows:
deploy_pipeline:
description: Lint, test, build, and deploy a service.
tags: [deploy, pipeline]
aliases: [ship, release]
examples:
- "Deploy the payments service to staging"
inputSchema:
type: object
required: [service]
properties:
service: { type: string }
environment:
type: string
enum: [staging, production]
default: staging
additionalProperties: false
initialState: lint
initialContext: # seed values for workflow context
attempts: 0
maxChainDepth: 10 # max deterministic transitions before halting (default: 50)
timeoutMs: 3600000 # workflow-level timeout (1 hour)
linkFilter: all # "all" or "byGuards" — controls which transitions appear as links
onTimeout: # what happens if the workflow times out
target: failed
executor:
kind: mcp
connection: notifier
tool: send_alert
states:
lint:
goal: Validate code quality
guidance: This step runs automatically.
transitions:
run_lint:
title: Run linter
target: test
actor: deterministic # runs automatically, no model decision
executor:
kind: cli
command: lint-check
args: ["$.input.service"]
output:
lintPassed: "$.output.json.passed"
test:
transitions:
run_tests:
title: Run test suite
target: ready_to_deploy
actor: deterministic
executor:
kind: cli
command: test-runner
output:
testsPassed: "$.output.json.passed"
ready_to_deploy:
linkFilter: byGuards # per-state override — only show transitions whose guards pass
transitions:
deploy:
title: Deploy to environment
target: deployed
actor: agent
guards:
- { kind: expr, expr: "$.context.testsPassed == true" }
executor:
kind: cli
command: deploy
args: ["$.context.artifactId"]
output:
deploymentId: "$.output.json.deploymentId"
reliability:
timeoutMs: 60000
retry:
maxAttempts: 3
backoff: exponential
initialDelayMs: 2000
abort:
title: Abort deployment
target: aborted
deployed:
terminal: true
aborted:
terminal: true
failed:
terminal: true

Each transition supports:

KeyPurpose
targetDestination state (required)
titleHuman-readable name, shown in links
descriptionLonger explanation
actorWho can trigger: agent, human, system, deterministic (default: agent)
inputSchemaJSON Schema for transition arguments
guardsArray of guards that must all pass
executorWhat runs when the transition fires (see executors)
outputMaps executor output into workflow context
evidenceRequires specific evidence artifacts
reliabilityTimeout, retry, and fallback policies
prefillPre-resolved argument values for links, reducing what the model has to generate
branchesAuto-branch destination based on executor result (first matching when guard wins, otherwise falls back to target)
ActorBehavior
agentDefault. The model decides when to take this transition.
humanOnly a human principal can submit. The gateway rejects agent submissions with ACTOR_MISMATCH.
systemReserved for system-triggered transitions.
deterministicThe runtime chains through this automatically — no model decision needed. Multiple deterministic transitions execute in a single round trip.

Controls where audit events go. Every workflow action emits structured JSON events automatically.

audit:
sink: stderr # stderr | file | memory | none
path: ./audit-logs # required when sink is "file" — a DIRECTORY
rotation: daily # hourly | daily | weekly (default: daily)

When sink: file, path is a directory of date-rotated log files, not a single .jsonl. The rotation key controls how often a new file is started (hourly, daily, or weekly); it defaults to daily.

See audit events for the full event reference.

Controls what gets indexed for search.

discovery:
include: # what to index (default: ["proxy", "workflows"])
- proxy # proxy capabilities (default: included)
- workflows # workflow definitions (default: included)
- connections # raw connections (default: not included)

include is the only honored key. By default, proxy capabilities and workflows are indexed. Add connections if you want raw connections to appear in search results too. An unrecognized token (e.g. workflow instead of workflows) fails the config with INVALID_DISCOVERY_INCLUDE rather than silently dropping a category.

Where workflow instances are persisted. See stores for the full reference.

store:
kind: memory # memory | file | sqlite
path: ./workflows.db # filesystem path — used by file and sqlite

path is the filesystem location for file (a directory) and sqlite (a database file); memory ignores it. Only sqlite keeps Evidence and acknowledgment stores durable. A serve deployment refuses kind: file (it would lose governance state on restart) unless gateway.allow_ephemeral: true is set for dev.

Optional. Enables semantic candidate ranking for lexicon SUBJECT_NEEDS_DEFINITION interactions (SPEC §30.10.10). Omit the block entirely (or set backend: none) to use lexical-only ranking.

embeddings:
backend: ollama # none (default) | ollama | openai_compatible
url: http://localhost:11434/api/embeddings # required when backend != none
model: nomic-embed-text # provider's model name
dimensions: 768 # required (>0) when backend != none
api_key_env: OPENROUTER_API_KEY # optional; absent for unauthenticated local services

When a backend other than none is set, the gateway POSTs to the configured URL at lexicon-write time to compute embeddings, and at SUBJECT_NEEDS_DEFINITION time to rank candidates semantically. ollama and openai_compatible select the request/response format. On backend failure, lexicon writes are rejected with EMBEDDING_BACKEND_FAILED. See Lexicon and Discovery for the tiered ranking details.