Skip to Content
ReferenceConfig File

Config File

Every gestaltd deployment reads one or more YAML config files. The current config API version is gestaltd.config/v6, and every config file must declare it with apiVersion. When you pass multiple --config flags, Gestalt merges them left-to-right before validation and bootstrap. The top-level keys are server (listener, encryption, egress, and host-provider selection), authorization (shared subject access policy), connections (reusable upstream credential definitions), providers (named authentication, secrets, telemetry, audit, UI, indexeddb, authorization, workflow, cache, s3, and agent entries), providerRepositories (package index URLs), runtime (hosted execution backends), workflows (config-managed schedules and triggers), and apps (executable integrations and optional app-backed UI mounts):

apiVersion: gestaltd.config/v6 server: # listener, encryption, egress, host-provider selectors authorization: policies: {} connections: {} providerRepositories: {} runtime: providers: {} providers: agent: {} audit: {} authentication: {} authorization: {} cache: {} indexeddb: {} s3: {} secrets: {} telemetry: {} ui: {} workflow: {} workflows: schedules: {} eventTriggers: {} apps: {}

Load Semantics

${ENV_VAR} references are expanded before YAML decoding. When a referenced variable is unset, Gestalt checks for a corresponding ENV_VAR_FILE variable and reads that file path instead. If neither is set, config loading fails. Use ${ENV_VAR:-} when an explicit empty default is intentional. Unknown YAML fields are rejected.

When multiple config files are loaded, Gestalt applies these merge rules:

  • maps deep-merge left-to-right
  • later scalar values win
  • later lists replace inherited lists
  • null deletes an inherited key

Relative file paths resolve relative to the config file that introduced them. That includes local source paths, iconFile, and server.artifactsDir. Lockfiles and, by default, artifact directories stay rooted at the leftmost config file. --lockfile overrides only the lockfile path.

Several fields carry defaults when omitted: server.public.port defaults to 8080; the runtime secrets manager defaults to built-in env when providers.secrets is omitted; providers.telemetry.default defaults to built-in stdout; and providers.audit.default defaults to built-in inherit. Structured secret refs are resolved during bootstrap, not during YAML parsing.

authorization configures shared subject access policy at the top level.

Layered example

Base config:

server: encryptionKey: ${GESTALT_ENCRYPTION_KEY} public: port: 8080 apps: github: source: ./apps/github/manifest.yaml egress: allowedHosts: - api.github.com - uploads.github.com

Override:

server: management: port: 9090 apps: github: egress: allowedHosts: - api.github.com iconFile: ./icons/github.svg displayName: null

Run:

gestaltd validate --config ./base.yaml --config ./overrides/local.yaml

The resulting config keeps server.public.port, adds server.management.port, replaces apps.github.egress.allowedHosts, resolves iconFile relative to ./overrides/local.yaml, and deletes the inherited displayName.

providerRepositories

providerRepositories declares package indexes that this project can use for package sources. The built-in valon repository points at the first-party provider index, so most first-party packages do not need a project-level repository entry.

providerRepositories: valon: url: https://registry.gestaltd.ai/provider-index.yaml

Use gestaltd provider repo add NAME URL --config ./gestalt.yaml to write this section from the CLI. Without --config, repository commands edit the user-level repository store instead. When the same repository name appears in both places, project config supplies the URL and the user-level store can still supply a token.

FieldDescription
providerRepositories.<name>.urlHTTP(S) or file:// URL for a gestaltd-provider-index YAML file.

providerSnapshotRepositories

providerSnapshotRepositories declares immutable artifact roots for commit-pinned git provider sources. Snapshot repositories are used by source.git entries that set artifactRepository.

providerSnapshotRepositories: valon: url: https://storage.googleapis.com/valon-gestalt-provider-snapshots gestaltRef: 651a5c30feb995c9364c38f63d0d5c3880bc2055
FieldDescription
providerSnapshotRepositories.<name>.urlHTTP(S) root containing deterministic snapshot provider-release.yaml files and archives.
providerSnapshotRepositories.<name>.gestaltRefOptional full Gestalt commit SHA expected for snapshots from this repository.

server

server: public: host: 0.0.0.0 port: 8080 management: host: 127.0.0.1 port: 9090 baseUrl: https://gestalt.example.com encryptionKey: secret: provider: default name: gestalt-encryption-key apiTokenTtl: 30d artifactsDir: ./state runtime: defaultProvider: kubernetes relayBaseUrl: https://gestalt.example.com agent: defaultToolNarrowingThreshold: 200 providers: authorization: indexeddb authentication: oidc indexeddb: main authorization: policies: support_staff: default: deny members: - subjectID: user:viewer-user role: viewer - subjectID: user:admin-user role: admin - subjectID: service_account:triage-bot role: viewer

public.host and public.port control the public listener address. Both default to all-interfaces on port 8080.

management.host and management.port bind a separate listener for operator traffic. When management.port is omitted, /admin and /metrics stay on the public listener, a mode best kept to local development or other trusted-network deployments. For production, prefer configuring server.management so the operator surface leaves the public listener entirely.

management.baseUrl is the browser-facing absolute URL for the management listener. It is only needed when /admin is protected with server.admin.authorizationPolicy on a split public/management deployment. In that mode, unauthenticated management /admin requests round-trip through the public listener’s login flow and return to the built-in management /admin route after callback. management.baseUrl must share a hostname with server.baseUrl so the session cookie can be reused across listeners. If server.baseUrl uses https, management.baseUrl must also use https.

baseUrl derives OAuth callback URLs when explicit redirect URLs are omitted, and gives the management admin UI an absolute link back to the public client UI. When unset, the management admin UI omits the Client UI link rather than sending operators to a broken same-origin /.

encryptionKey is the only required server field. It is the root deployment secret, used for encryption and derived session material.

apiTokenTtl controls the lifetime of newly issued API tokens, defaulting to 30d. It accepts Go duration strings (1h, 720h) and day suffixes (30d). artifactsDir is a writable directory for prepared artifacts produced by gestaltd sync --locked and consumed by serve --locked when no CLI override is supplied; it defaults to the config file’s directory. See Configuration for the full lock/sync/serve lifecycle.

runtime.defaultProvider selects the runtime provider used when a app, agent provider, or workflow provider opts into hosted execution without naming a provider. runtime.relayBaseUrl overrides the URL handed to hosted runtimes for Gestalt host-service callbacks and hostname egress proxying. It defaults to server.baseUrl; set it when hosted runtimes need to call back through a private listener or internal reverse proxy while browser and OAuth flows still use the public origin. The target may be the management listener when that listener is reachable from the hosted runtime network.

agent.defaultToolNarrowingThreshold controls implicit default MCP catalog grants for agent providers that support the catalog. When the total visible catalog is larger than this threshold and the latest user message exactly names a provider, Gestalt grants that provider instead of a global wildcard. Omit it to use the server default; set it to 0 to narrow whenever any visible catalog tool exists.

admin.authorizationPolicy binds the built-in /admin route to one shared subject access policy from authorization.policies. admin.allowedRoles controls which roles may load the built-in admin UI, defaulting to [admin].

admin.ui optionally pins /admin to one named providers.ui.<name> bundle. The selected bundle must ship admin/index.html inside its prepared asset root. When admin.ui is omitted, Gestalt first auto-discovers admin assets from the root-mounted UI bundle (path: /) when that bundle contains admin/index.html, then falls back to the built-in admin shell.

authorization.policies configures shared subject access policies. Each policy resolves a caller to exactly one role by matching canonical subjectID values such as user:<id> or service_account:<id>, then applies a default fallback (allow or deny) when no member matches. Apps opt into a policy explicitly through authorizationPolicy.

authorization.models defines deployment-owned authorization model fragments as generic resource types, relations, actions, and allowed targets. authorization.relationships defines static relationship tuples against those resource types. These fields are parsed and validated alongside existing authorization.policies; provider reconciliation consumes both the compatibility policy inputs and the generic model fragments when the authorization composer is enabled.

authorization: models: default: resourceTypes: team: relations: member: subjectTypes: [subject] admin: subjectTypes: [subject] actions: view: relations: [member, admin] manage: relations: [admin] dynamic: allowAdditionalRelationships: true managed_subject: relations: viewer: subjectTypes: [subject] editor: subjectTypes: [subject] admin: subjectTypes: [subject] actions: view: relations: [viewer, editor, admin] manage: relations: [admin] app/github/repository: relations: reader: allowedTargets: - subjectType: subject maintainer: allowedTargets: - subjectType: subject actions: read: relations: [reader, maintainer] administer: relations: [maintainer] relationships: - subject: type: subject id: user:alice relation: admin resource: type: team id: servicing source: layer: static_config id: authorization.relationships[0]

Dynamic grants are app-scoped, not policy-scoped. When a app sets authorizationPolicy, built-in Gestalt admins can manage additional dynamic members from the admin UI at /admin/?tab=members, and callers with role admin on a specific app can manage dynamic members for that app through /admin/api/v1/authorization/apps/{app}/members or gestalt authorization apps members .... Static policy members always win over dynamic grants, and Gestalt rejects dynamic writes for subjects that already have static authorization on that app. The email write field is a user-only convenience alias; subjectId accepts any canonical non-system subject ID, such as user:<id>, service_account:<id>, or another deployment-defined subject kind.

Non-human callers are modeled as service account subjects, typically service_account:<id>. Grant app access with authorization.policies or provider-backed dynamic memberships using the canonical subject ID, and store third-party credentials through the configured external credentials provider under the same subject ID.

Apps without authorizationPolicy are open to any authenticated subject. Bind a app to a policy when non-human access should be explicit.

FieldDefaultDescription
public.host0.0.0.0Bind address for the public listener.
public.port8080Port for the public listener.
management.hostBind address for the management listener.
management.portPort for the management listener. Required when management.host is set.
management.baseUrlBrowser-facing absolute URL for the management listener. Required for protected built-in /admin on split public/management deployments.
baseUrlPublic origin URL used for OAuth callbacks and admin UI links.
encryptionKeyRequired. Root encryption key. Typically a structured secret ref.
apiTokenTtl30dLifetime of newly issued API tokens. Go durations or day suffixes (30d).
artifactsDirconfig directoryDirectory for prepared artifacts produced by gestaltd sync --locked.
agent.defaultToolNarrowingThreshold200Visible catalog size above which implicit default agent catalog grants may be narrowed to exactly mentioned providers. Set 0 to narrow whenever a matching provider has any visible tools.
providers.audit / providers.authentication / providers.authorization / providers.indexeddb / providers.secrets / providers.telemetryOptional selectors for the named host-provider entries under providers.*. Required for IndexedDB whenever more than one entry exists and recommended for other host-provider maps when more than one entry exists.
dev.attachmentStateEnables private remote provider-dev attach routes and selects where attachment state is stored. The only supported value is indexeddb, which stores attachment state in the configured host IndexedDB for shared and load-balanced servers.
runtime.defaultProviderDefault runtime-provider selector for apps and providers that opt into runtime. This does not move anything into hosted runtimes by itself.
runtime.relayBaseUrlserver.baseUrlAbsolute HTTP(S) URL that hosted runtimes use for Gestalt host-service relay and egress proxy callbacks.
admin.authorizationPolicyShared subject access policy applied to the built-in /admin route.
admin.allowedRoles[admin]Roles allowed to load the built-in /admin route when admin.authorizationPolicy is set.
admin.uiOptional named providers.ui bundle whose admin/ assets should back /admin. When omitted, Gestalt auto-discovers admin/ assets from the root UI bundle before using the built-in fallback.
authorization.policiesShared subject access policies resolved by canonical subjectID.
authorization.modelsStatic authorization model fragments keyed by model name. Each fragment defines resourceTypes, relation subjectTypes or allowedTargets, and actions.
authorization.relationshipsStatic relationship tuples with subject, relation, and resource fields.
authorization.resourceTypes.<type>.dynamic.allowAdditionalRelationshipsfalseAllows later dynamic source state to add relationship tuples for a static resource type. Dynamic override/removal of static relationships is not supported.

When server.management is configured, Gestalt serves /, /api/v1/*, auth routes, and /mcp on the public listener, while /admin, /admin/api/v1/*, /metrics, /health, and /ready move to the management listener. The management listener can also serve token-authenticated hosted-runtime host-service relay and egress proxy callbacks when server.runtime.relayBaseUrl points at that origin.

The management listener is intended to be protected by deployment infrastructure such as private networking, VPN, or an internal-only reverse proxy. Gestalt does not apply its own session or API-token auth to /metrics on the management listener. When server.admin.authorizationPolicy is set, Gestalt does apply browser session auth plus role-based policy checks to /admin and /admin/api/v1/*.

runtime

runtime.providers defines named execution backends for runtime-placed providers. These backends are only used when a app, agent provider, or workflow provider opts into runtime. If a provider omits runtime, it still runs on the same machine as gestaltd.

See Providers > Runtime for the lifecycle and capability model behind these fields.

runtime: providers: local: driver: local kubernetes: source: path: ./vendor/gestalt-providers/runtime/kubernetes/manifest.yaml default: true config: namespace: gestalt-runtime container: runtime connectionMode: portForward

server.runtime.defaultProvider supplies the default runtime name for apps, agent providers, or workflow providers that set runtime without an explicit runtime.provider.

FieldDescription
providers.<name>.driverOptional built-in runtime selector. The only built-in runtime is local.
providers.<name>.sourcePackage source, local manifest path, metadata URL, source.url, or source.githubRelease for an installable kind: runtime provider such as Kubernetes.
providers.<name>.defaultMarks the runtime as the default selection when server.runtime.defaultProvider is omitted. Only one runtime may be default.
providers.<name>.configRuntime-provider-specific config blob. Built-ins such as local reject custom config entirely.

runtime.providers.*.config for the Kubernetes runtime provider

The Kubernetes runtime provider maps one Gestalt runtime session to one native Kubernetes Pod and connects to the app over port-forwarding, Pod IP networking, or Service DNS.

runtime: providers: kubernetes: source: path: ./vendor/gestalt-providers/runtime/kubernetes/manifest.yaml config: namespace: gestalt-runtime container: runtime appPort: 50051 connectionMode: portForward sessionReadyTimeout: 3m execTimeout: 2m cleanupTimeout: 30s
FieldDescription
namespaceKubernetes namespace where runtime session Pods are created.
containerContainer name that contains the app executable and socket bridge tooling.
appPortTCP port exposed by the hosted app gRPC server inside the Pod.
connectionModeHow gestaltd connects to the hosted app: portForward, podIP, or serviceDNS.
sessionReadyTimeoutHow long to wait for the runtime Pod to become ready.
execTimeoutTimeout for Kubernetes exec calls used to launch or stop the app process.
cleanupTimeoutTimeout for runtime session cleanup.

When an app uses a hosted runtime provider such as Kubernetes, apps.<name>.runtime.image or apps.<name>.runtime.template is required. Hosted runtimes can run apps that need Gestalt-owned services or hostname-based egress when the host is configured for the public relay and proxy path. In practice that means server.baseUrl (or server.runtime.relayBaseUrl) and server.encryptionKey must be set, and the runtime must report a compatible support profile. Without those prerequisites, Gestalt will reject apps that need relay-backed host services, egress.allowedHosts, or a server-wide default-deny egress policy. When hosted runtimes cannot reach server.baseUrl directly, set server.runtime.relayBaseUrl to the private HTTP(S) origin they should use for host-service relay and egress proxy callbacks. The origin can point at the management listener or another internal reverse proxy that reaches gestaltd.

Hosted runtime images are expected to contain the provider package at the image working directory. Gestalt starts the executable declared by the provider manifest entrypoint and passes the manifest entrypoint args:

apps: support: source: package: github.com/valon-technologies/gestalt-providers/app/httpbin version: 0.0.1-alpha.1 runtime: provider: kubernetes image: ghcr.io/example/support-app@sha256:...

There is no separate launch source or runtime command override. If runtime.image is set, the image must preserve the provider package layout and make the manifest entrypoint executable from the image working directory.

providers.authentication

Platform authentication is optional. Omit the providers.authentication block entirely to disable it. When platform authentication is enabled, configure one or more named entries under providers.authentication. If more than one entry exists, set server.providers.authentication to pick which one the host should use.

local

The local authentication provider is intended for single-user development on a trusted machine. It uses the same ProviderEntry shape as any other providers.authentication.<name> entry, but it does not require an external identity provider configuration.

oidc

providers: authentication: oidc: source: package: github.com/valon-technologies/gestalt-providers/auth/oidc version: 0.0.1-alpha.1 config: issuerUrl: https://login.example.com clientId: ${OIDC_CLIENT_ID} clientSecret: secret: provider: default name: oidc-client-secret redirectUrl: https://gestalt.example.com/api/v1/auth/login/callback allowedDomains: - example.com scopes: - openid - email - profile sessionTtl: 24h pkce: true displayName: Company SSO allowInsecureHttp: false pkceVerifierTtl: 1h pkceVerifierMaxItems: 10000

Two fields are required: issuerUrl (the OIDC discovery base URL) and clientId. clientSecret may be omitted for PKCE-only public clients.

redirectUrl defaults to a path derived from server.baseUrl. allowedDomains restricts login by email domain. scopes defaults to openid, email, and profile.

sessionTtl controls session token lifetime and defaults to 24h. Set pkce to true to enable Proof Key for Code Exchange. displayName controls the label shown in the login UI, defaulting to SSO.

issuerUrl and all discovered OIDC endpoints must use https:// by default. Set allowInsecureHttp: true only for local loopback development against issuers such as http://127.0.0.1:8080 or http://localhost:8080. Non-loopback http:// endpoints are always rejected.

pkceVerifierTtl and pkceVerifierMaxItems are optional PKCE cache controls. They default to 1h and 10000 in-flight login attempts, and both must be greater than zero when set.

OIDC checks email_verified when the claim is present. Unverified accounts are rejected.

providers.authentication shape

Authentication providers use the same ProviderEntry shape under providers.authentication.<name>. First-party authentication providers are published under github.com/valon-technologies/gestalt-providers/auth/.

FieldDescription
defaultMarks this named authentication entry as the default selection when server.providers.authentication is omitted.
sourcePackage source, local provider manifest path, published provider-release.yaml metadata URL, source.url, or source.githubRelease for a GitHub-hosted release asset.
source.authOptional metadata-fetch auth for package repositories, direct metadata URLs, local release metadata, and source.githubRelease.
configProvider-specific config passed into the authentication provider.
envExtra environment variables passed to the provider process.
egress.allowedHostsOutbound host allowlist. For executable apps, this is only a complete boundary when a supported sandboxed runtime is active.

providers.indexeddb

Indexed databases are provider-based. Configure one or more named entries under providers.indexeddb, then set server.providers.indexeddb to the name the host should use for system storage. First-party indexeddb providers are published at github.com/valon-technologies/gestalt-providers/indexeddb/<name>.

server: providers: indexeddb: main providers: indexeddb: main: source: package: github.com/valon-technologies/gestalt-providers/indexeddb/relationaldb version: 0.0.1-alpha.2 config: dsn: ${DATABASE_URL}

The exact config fields depend on the selected external indexeddb provider package. Gestalt itself does not have a built-in indexeddb driver matrix; it loads the configured provider process and passes the selected providers.indexeddb.<name>.config block through to that provider.

Common first-party IndexedDB packages:

ProviderPackage pathConfig fields
RelationalDBgithub.com/valon-technologies/gestalt-providers/indexeddb/relationaldbdsn
DynamoDBgithub.com/valon-technologies/gestalt-providers/indexeddb/dynamodbtable, region, optional endpoint
MongoDBgithub.com/valon-technologies/gestalt-providers/indexeddb/mongodburi, optional database

providers.authorization

Authorization providers are host-scoped policy engines and relationship stores. Configure one or more named entries under providers.authorization, then set server.providers.authorization to the name Gestalt should use for dynamic subject authorization decisions, relationship storage, model lifecycle, and built-in admin authorization APIs. First-party authorization providers are published at github.com/valon-technologies/gestalt-providers/authorization/<name>.

server: providers: indexeddb: main authorization: indexeddb providers: indexeddb: main: source: package: github.com/valon-technologies/gestalt-providers/indexeddb/relationaldb version: 0.0.1-alpha.2 config: dsn: ${DATABASE_URL} authorization: indexeddb: source: package: github.com/valon-technologies/gestalt-providers/authorization/indexeddb version: 0.0.1-alpha.1 config: indexeddb: main

Authorization providers are optional only when you are relying on static human policy members from config. When configured, Gestalt uses the selected provider for dynamic subject app/admin authorization, relationship storage, model lifecycle, and the built-in admin authorization APIs under /admin/api/v1/authorization/....

If providers.authorization is omitted, static policy members under authorization.policies still work, but there is no provider-backed relationship store and the dynamic subject authorization control plane is unavailable.

The exact config fields depend on the selected authorization provider package. The first-party authorization/indexeddb provider expects the name of a host IndexedDB provider and stores authorization models plus relationships there.

FieldDescription
defaultMarks this named authorization entry as the default selection when server.providers.authorization is omitted.
sourcePackage source, local provider manifest path, published provider-release.yaml metadata URL, source.url, or source.githubRelease for a GitHub-hosted release asset.
source.authOptional inline source auth for package repositories, remote release sources, direct metadata URLs, and source.githubRelease.
configProvider-specific config passed into the authorization provider.
envExtra environment variables passed to the provider process.
egress.allowedHostsOutbound host allowlist. For executable apps, this is only a complete boundary when a supported sandboxed runtime is active.

providers.workflow

Workflow providers back global runs, schedules, and triggers. Configure one or more named entries under providers.workflow, then reference them from top-level workflows.schedules.*.provider or workflows.eventTriggers.*.provider. There is no server.providers.workflow: config-managed workflow objects choose a provider explicitly, or inherit the sole/default workflow provider when provider is omitted. User-owned schedules and triggers use the same provider pool through /api/v1/workflow/schedules, /api/v1/workflow/event-triggers, and gestalt workflow ....

providers: indexeddb: workflow_state: source: package: github.com/valon-technologies/gestalt-providers/indexeddb/relationaldb version: 0.0.1-alpha.2 config: dsn: ${DATABASE_URL} workflow: local: source: package: github.com/valon-technologies/gestalt-providers/workflow/indexeddb version: 0.0.1-alpha.1 indexeddb: provider: workflow_state db: workflow objectStores: - schedules - event_triggers - runs config: pollInterval: 1s workflows: schedules: nightly_sync: provider: local cron: "0 3 * * *" timezone: America/New_York runAs: subject: id: service_account:roadmap-sync target: steps: - id: sync_items app: name: roadmap operation: sync_items eventTriggers: item_updated: provider: local match: type: roadmap.item.updated target: steps: - id: sync_items app: name: roadmap operation: sync_items

providers.workflow.<name>.indexeddb is optional. When present, it binds a named host IndexedDB provider into the workflow provider. indexeddb.db defaults to the workflow provider name, and indexeddb.objectStores can allowlist logical stores exposed through that binding.

Workflow providers can also opt into hosted execution. gestaltd serve starts hosted workflow workers automatically after the provider graph is ready; there is no separate worker command to run. A hosted workflow provider may set runtime.pool to keep a fixed pool of runtime sessions ready for polling backends such as Temporal. Workflow runtime pools are fixed-size: maxReadyInstances must match minReadyInstances.

providers: workflow: temporal: source: package: github.com/valon-technologies/gestalt-providers/workflow/temporal version: 0.0.1-alpha.1 runtime: provider: gke template: workflow-workers pool: minReadyInstances: 2 maxReadyInstances: 2 startupTimeout: 5m healthCheckInterval: 30s restartPolicy: always drainTimeout: 2m config: namespace: default runtime: providers: gke: source: package: github.com/valon-technologies/gestalt-providers/runtime/gke version: 0.0.1-alpha.1
FieldDescription
defaultMarks this named workflow entry as the default selection when a config-managed workflow object or API request omits provider.
sourcePackage source, local provider manifest path, published provider-release.yaml metadata URL, source.url, or source.githubRelease for a GitHub-hosted release asset.
source.authOptional inline source auth for package repositories, remote release sources, direct metadata URLs, and source.githubRelease.
configProvider-specific config passed into the workflow provider.
indexeddbOptional host IndexedDB binding for the workflow provider, including provider, optional db, and optional objectStores.
envExtra environment variables passed to the provider process.
runtimeOptional runtime placement for this workflow provider. Supports provider, template, image, imagePullAuth, metadata, and pool. pool contains hosted workflow worker lifecycle fields: minReadyInstances, maxReadyInstances, startupTimeout, healthCheckInterval, restartPolicy, and drainTimeout.
egress.allowedHostsOutbound host allowlist. For executable apps, this is only a complete boundary when a supported sandboxed runtime is active.

providers.agent

Agent providers back global agent sessions and turns. Configure one or more named entries under providers.agent. There is no server.providers.agent: each session request either selects a provider explicitly or inherits the sole/default providers.agent entry when provider is omitted. The same provider pool is used by the global agent API/CLI and by apps that call the host agent manager.

providers: agent: simple: source: ./providers/agent/simple/manifest.yaml default: true indexeddb: provider: default db: simple_agent runtime: provider: kubernetes image: ghcr.io/valon/gestalt-python-runtime:latest workspace: prepareTimeout: 10m git: allowedRepositories: - github.com/valon-technologies/* pool: minReadyInstances: 1 maxReadyInstances: 4 startupTimeout: 2m healthCheckInterval: 30s restartPolicy: always drainTimeout: 1m config: runStore: runs idempotencyStore: run_idempotency defaultModel: fast aliases: fast: openai/gpt-4.1-mini deep: openai/gpt-5.5

Agent providers receive canonical session and turn requests with messages, optional resolved tools, optional responseSchema, and provider-specific config. Agent providers that need durable state can opt into a host IndexedDB binding with indexeddb; the provider process then uses the SDK IndexedDB helper against GESTALT_INDEXEDDB_SOCKET.

Agent providers may also opt into a hosted runtime with runtime, using the same runtime-provider selection model as executable apps. When providers.agent.<name>.runtime is set, gestaltd starts the agent provider inside the selected hosted runtime instead of launching it directly on the gestaltd host. Tool callbacks still route back through the host agent socket, and egress.allowedHosts remains the operator-facing egress policy.

When hosted agents accept caller-supplied workspaces, runtime.workspace controls the pre-session checkout step. Gestalt validates the requested repositories against workspace.git.allowedRepositories, then asks the selected runtime provider to prepare the checkout before calling the agent provider’s CreateSession. workspace.prepareTimeout defaults to 10m.

Local gestalt agent launches the selected provider’s configured harness on the operator’s machine. defaultHarness selects a named entry under harnesses; each harness has command, optional args, optional env, optional workingDirectory, optional requiredCommands, and optional install metadata. install.instructions and install.commands are returned to the CLI when a required local command is missing. Install commands may use either structured command plus args, or shell for installers that must run through the platform shell.

FieldDescription
defaultMarks this named agent entry as the default selection when a session request omits provider.
sourcePackage source, local provider manifest path, published provider-release.yaml metadata URL, source.url, or source.githubRelease for a GitHub-hosted release asset.
source.authOptional inline source auth for package repositories, remote release sources, direct metadata URLs, and source.githubRelease.
indexeddbOptional agent IndexedDB config. indexeddb.provider selects the named providers.indexeddb entry backing the agent’s single IndexedDB SDK socket; unlike apps, agents do not inherit the selected/default host IndexedDB automatically. indexeddb.db overrides the provider-visible database name and defaults to the agent provider key. indexeddb.objectStores optionally allowlists logical object stores the agent may use.
configProvider-specific config passed into the agent provider.
envExtra environment variables passed to the provider process.
defaultHarnessOptional default local harness name used by gestalt agent and gestalt agent doctor when --harness is omitted.
harnessesOptional named local harness configs for gestalt agent. Each harness can define command, args, env, workingDirectory, requiredCommands, and install guidance.
runtimeOptional runtime placement for this agent provider. Supports provider, template, image, imagePullAuth, metadata, workspace, and pool. imagePullAuth.dockerConfigJson contains Docker config JSON used by runtime providers when pulling private OCI images. workspace.prepareTimeout bounds runtime workspace preparation, and workspace.git.allowedRepositories is an allowlist of canonical Git repository identities or path-style wildcards such as github.com/valon-technologies/*. pool contains hosted agent lifecycle fields: minReadyInstances, maxReadyInstances, startupTimeout, healthCheckInterval, restartPolicy, and drainTimeout.
egress.allowedHostsOutbound host allowlist. For executable providers, this is only a complete boundary when a supported sandboxed runtime is active.

workflows

Top-level workflows declares config-managed schedules and triggers. These objects are reconciled into the selected workflow provider at startup and invoke an explicit target.steps plan. Each step runs exactly one app call or one agent turn.

Config-managed workflows are deployment-owned. They are created from YAML at startup, updated when config changes, and removed when deleted from config. They are distinct from user-owned schedules created through the global workflow HTTP API and CLI. For an end-to-end usage guide, see Workflow.

workflows: schedules: nightly_sync: provider: local cron: "0 3 * * *" timezone: America/New_York target: steps: - id: sync_items app: name: roadmap operation: sync_items connection: default instance: tenant-a input: literal: mode: incremental invokes: - app: slack operation: conversations.list - app: slack operation: conversations.history paused: false eventTriggers: item_updated: provider: local match: type: roadmap.item.updated source: roadmap target: steps: - id: sync_items app: name: roadmap operation: sync_items input: literal: reason: sync runAs: subject: id: service_account:roadmap-events invokes: - app: slack operation: chat.postMessage paused: false

workflows.schedules.*

FieldDescription
providerOptional named providers.workflow entry. When omitted, Gestalt uses the sole/default workflow provider.
target.stepsRequired ordered workflow steps. Each step has id and exactly one of app or agent.
target.steps[].inputsOptional named values available to the step. Values use the same source shape as app input values.
target.steps[].app.nameTarget app key from apps. Required for app steps.
target.steps[].app.operationTarget operation ID. Required for app steps.
target.steps[].app.connectionOptional target connection name.
target.steps[].app.instanceOptional target instance name.
target.steps[].app.inputOptional workflow value that must resolve to a JSON object. Use literal for static params.
target.steps[].agentOptional agent turn using provider, model, sessionKey, prompt, messages, tools, responseSchema, and modelOptions.
target.steps[].whenOptional guard with value and scalar equals; value.stepOutput.stepId may reference only earlier steps.
runAs.subject.idOptional service-account subject ID, such as service_account:roadmap-sync, used as the runtime principal and credential subject. Without runAs, config-managed workflows run as system:config and cannot target subject-scoped credentials.
runAs.subject.kindOptional subject kind. When set, it must match the kind in runAs.subject.id; declarative workflows currently support only service_account.
runAs.subject.displayName / runAs.subject.authSourceOptional metadata copied into the runtime principal and run-as audit fields.
invokesOptional additional app-operation grants as { app, operation } entries. The direct target operation is always included. These grants are a token permission ceiling; they do not create credentials or replace apps.<target>.invokes for nested app calls.
cronRequired. Five-field cron expression.
timezoneIANA timezone name. Defaults to UTC.
pausedWhen true, Gestalt reconciles the schedule in a paused state.

workflows.eventTriggers.*

FieldDescription
providerOptional named providers.workflow entry. When omitted, Gestalt uses the sole/default workflow provider.
target.stepsRequired ordered workflow steps. Each step has id and exactly one of app or agent.
target.steps[].appApp step call fields: name, operation, optional connection, optional instance, and optional workflow-value input.
target.steps[].agentAgent step turn fields: provider, model, sessionKey, prompt, messages, tools, responseSchema, and modelOptions.
target.steps[].whenOptional guard with value and scalar equals; value.stepOutput.stepId may reference only earlier steps.
runAs.subject.idOptional service-account subject ID used as the runtime principal and credential subject. See runAs.
runAs.subject.kindOptional subject kind. When set, it must match the kind in runAs.subject.id; declarative workflows currently support only service_account.
runAs.subject.displayName / runAs.subject.authSourceOptional metadata copied into the runtime principal and run-as audit fields.
invokesOptional additional app-operation grants as { app, operation } entries. The direct target operation is always included. These grants are a token permission ceiling; they do not create credentials or replace apps.<target>.invokes for nested app calls.
match.typeRequired. Event type matcher.
match.sourceOptional event source matcher.
match.subjectOptional event subject matcher.
pausedWhen true, Gestalt reconciles the trigger in a paused state.

Event trigger matching is exact string matching. match.type is required; match.source and match.subject are optional exact-match filters. Event triggers can be config-managed through YAML or user-owned through the global workflow HTTP API and CLI.

providers.cache

Caches are provider-based and app-bound. Configure one or more named entries under providers.cache, then reference those names from apps.<name>.cache. Unlike IndexedDB, caches are not selected through server.providers.

providers: cache: session: source: package: github.com/valon-technologies/gestalt-providers/cache/valkey version: 0.0.1-alpha.1 config: address: ${VALKEY_ADDR} database: 0 apps: github: cache: - session

First-party cache providers are published at github.com/valon-technologies/gestalt-providers/cache/<name>.

FieldDescription
sourcePackage source, local provider manifest path, published provider-release.yaml metadata URL, source.url, or source.githubRelease for a GitHub-hosted release asset.
source.authOptional inline source auth for package repositories, remote release sources, direct metadata URLs, and source.githubRelease.
configArbitrary provider-specific config passed through to the cache provider.
envExtra environment variables passed to the provider process.
egress.allowedHostsOutbound host allowlist. For executable apps, this is only a complete boundary when a supported sandboxed runtime is active.

providers.s3

S3-compatible object stores are provider-based. Configure one or more named entries under providers.s3, then bind them into executable apps with apps.<name>.s3. Gestalt does not use S3 providers for its own system state; they are mounted only for app code.

providers: s3: assets: source: ./providers/s3/minio/manifest.yaml config: endpoint: http://127.0.0.1:9000 region: us-east-1 accessKeyId: ${MINIO_ROOT_USER} secretAccessKey: secret: provider: default name: minio-root-password apps: media: source: ./apps/media/manifest.yaml s3: - assets

The exact config fields depend on the selected S3 provider package. Gestalt’s portable S3 contract covers HeadObject, ReadObject, WriteObject, DeleteObject, ListObjects, CopyObject, and PresignObject, so a single app can target AWS S3, MinIO, Cloudflare R2, GCS interoperability mode, or other S3-compatible backends without changing application code.

Apps can also create host-mediated object access URLs for their scoped S3 bindings with SDK helpers such as TypeScript object.createAccessUrl(...) and Go object.CreateAccessURL(...). These URLs are served by gestaltd at /api/v1/s3/object-access/{token} and require server.baseUrl plus server.encryptionKey, because the host signs the logical app/binding/object scope before streaming bytes through the configured S3 provider.

providers.secrets

Structured secret refs name the provider explicitly:

encryptionKey: secret: provider: default name: gestalt-encryption-key

Configure one or more named entries under providers.secrets. Every config secret ref uses secret.provider to choose one of those named entries. When providers.secrets is omitted entirely, the runtime secrets manager defaults to built-in env, but structured refs still require an explicit named provider entry. server.providers.secrets continues to select the runtime host secrets provider when one is needed outside config resolution.

providers: secrets: default: source: file config: dir: /etc/gestalt-secrets
SourceConfig fieldsPurpose
envprefixReads secrets from environment variables. Default when providers.secrets is omitted.
filedirReads one secret per file from a base directory. Works with Kubernetes volume-mounted secrets.

Cloud secret backends

Cloud secret managers are external providers configured with package sources:

providers: secrets: gsm: source: package: github.com/valon-technologies/gestalt-providers/secrets/google version: 0.0.1-alpha.1 config: project: my-gcp-project
ProviderPackage pathConfig fields
Google Secret Managergithub.com/valon-technologies/gestalt-providers/secrets/googleproject (required), version
AWS Secrets Managergithub.com/valon-technologies/gestalt-providers/secrets/awsregion (required), versionStage, endpoint
HashiCorp Vaultgithub.com/valon-technologies/gestalt-providers/secrets/vaultaddress (required), token (required), mountPath, namespace
Azure Key Vaultgithub.com/valon-technologies/gestalt-providers/secrets/azurevaultUrl (required), version

See Secrets Providers for the complete reference and SDK interface.

Bootstrap credentials

Structured secret refs are resolved through the named secret manager at startup, but the secret manager’s own configuration (providers.secrets.<name>.config) cannot use secret refs because it would be self-referential. Use ${ENV_VAR} or ${ENV_VAR_FILE} placeholders for any credentials the secret manager itself needs.

Each provider authenticates to its backing service differently. The env and file providers are built in. The remaining rows apply when using the corresponding external provider from gestalt-providers:

ProviderAuthentication model
envReads from process environment. No external auth needed.
fileReads from local filesystem. No external auth needed.
Google Secret ManagerUses Application Default Credentials . In production, attach a service account or use workload identity. Locally, run gcloud auth application-default login.
AWS Secrets ManagerUses the AWS SDK default credential chain . In production, use an instance profile, ECS task role, or IRSA. Locally, run aws configure or set AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY.
HashiCorp VaultRequires explicit address and token in config. Inject the token via ${VAULT_TOKEN} or ${VAULT_TOKEN_FILE}.
Azure Key VaultUses DefaultAzureCredential. In production, use managed identity. Locally, run az login.

For local development, env or file avoids external dependencies entirely. Switch to a cloud secret manager when deploying to a managed environment where ambient identity is available.

providers.telemetry

Gestalt uses OpenTelemetry  for distributed traces, metrics, and structured logs. Every HTTP request, broker invocation, and app gRPC call is automatically traced. See Observability for the emitted metric inventory and Prometheus name translation.

Configure one or more named telemetry entries under providers.telemetry. When the block is omitted, Gestalt synthesizes providers.telemetry.default with built-in stdout. If you configure more than one entry, set server.providers.telemetry to pick the active one.

stdout

providers: telemetry: default: source: stdout config: format: text level: info serviceName: gestaltd

Outputs structured logs to standard output. Traces remain disabled, but metrics are collected locally and exposed through the Prometheus-compatible /metrics endpoint. The built-in admin UI at /admin reads that same scrape surface. This is the default when providers.telemetry is omitted.

FieldDefaultDescription
formattextLog format: text or json.
levelinfoMinimum log level: debug, info, warn, or error.
serviceNamegestaltdAttached to locally exposed metrics.
resourceAttributesExtra key-value pairs attached to local metrics.
metrics.prometheus.enabledtrueToggle the /metrics scrape endpoint.

otlp

providers: telemetry: default: source: otlp config: endpoint: otel-collector:4317 protocol: grpc serviceName: gestaltd insecure: false headers: Authorization: Bearer ${OTEL_TOKEN} resourceAttributes: deployment.environment: production service.version: "1.0.0" traces: samplingRatio: 1.0 metrics: interval: 60s logs: exporter: otlp level: info

Exports to any OpenTelemetry-compatible collector (Jaeger, Grafana Alloy, Datadog Agent) while still keeping local /metrics available by default. The built-in admin UI at /admin reads that same local scrape surface.

FieldDefaultDescription
endpointSDK defaultOTLP collector address.
protocolgrpcTransport protocol: grpc or http.
insecurefalseSkip TLS verification when true.
headersExtra headers on every export request.
serviceNamegestaltdOpenTelemetry service name.
resourceAttributesArbitrary key-value pairs as OTel resource attributes.
traces.samplingRatio1.0Fraction of traces sampled, from 0.0 to 1.0.
metrics.interval60sOTLP metric export interval.
metrics.prometheus.enabledtrueToggle the local /metrics scrape endpoint.
logs.exporterotlpWhere logs go: otlp or stdout.
logs.levelinfoMinimum log level.
logs.formattextLog format when exporter is stdout: text or json.

Provider process telemetry

Telemetry is a host provider, not an invocable runtime provider. gestaltd uses the selected providers.telemetry entry to configure its own OpenTelemetry SDKs and to derive standard OTEL_* environment for executable provider processes. Providers should not configure a separate exporter in their own YAML; they should initialize their language’s OpenTelemetry SDK from the environment that the host injects.

When the selected telemetry provider is otlp, executable providers receive values such as OTEL_EXPORTER_OTLP_ENDPOINT, OTEL_EXPORTER_OTLP_PROTOCOL, OTEL_TRACES_SAMPLER, OTEL_TRACES_SAMPLER_ARG, OTEL_METRIC_EXPORT_INTERVAL, OTEL_SERVICE_NAME=gestalt-provider-<provider>, and OTEL_RESOURCE_ATTRIBUTES containing service.namespace=gestalt-providers and gestaltd.provider.name=<provider>. Python providers using gestalt-sdk get that OpenTelemetry setup automatically from the SDK runtime and can use from gestalt import telemetry for GenAI semantic-convention helpers around model calls, agent turns, and tool execution.

To keep system logs on stdout while still exporting traces and metrics over OTLP:

providers: telemetry: default: source: otlp config: endpoint: otel-collector:4317 protocol: grpc metrics: interval: 60s logs: exporter: stdout format: json level: info

noop

providers: telemetry: default: source: noop

Disables all telemetry. Instrumentation points remain but produce zero overhead. This disables Prometheus scraping. /metrics returns a clear unavailable response, and the built-in admin UI shows that metrics are unavailable.

Trace spans

LayerSpan nameKey attributes
HTTPgestaltd: {method} {route}Standard HTTP semantic conventions plus resolved gestaltd.* request-surface attributes
Brokerbroker.invokegestalt.provider, gestalt.operation, gestalt.subject_id, gestalt.connection_mode
gRPC app and host service trafficPer-RPC spansStandard RPC semantic conventions plus gestaltd.rpc.role, gestaltd.provider.name, and, for host services, gestaltd.host_service.name

providers.audit

Audit records are emitted as structured log entries with log.type=audit. Configure one or more named entries under providers.audit; when the block is omitted, Gestalt synthesizes providers.audit.default with built-in inherit. If you configure more than one entry, set server.providers.audit to pick the active one. See Audit Logging for the event schema and coverage details, and Observability for export options. If you keep providers.audit.default.source: inherit, you can still route audit traffic to a dedicated backend by filtering on log.type=audit in your collector.

inherit

providers: audit: default: source: inherit

This is the default when providers.audit is omitted. Audit records follow the same log route as providers.telemetry: when telemetry uses stdout, audit logs go to standard output; when telemetry uses otlp, audit logs export through the OTLP log pipeline; when telemetry uses noop, audit output is disabled unless you override it here.

providers.audit.<name>.config is not allowed when providers.audit.<name>.source is inherit.

stdout

providers: audit: default: source: stdout config: format: json

Writes audit records to standard output even if the main telemetry pipeline is using a different provider.

FieldDefaultDescription
formattextOutput format: text or json.
levelinfoMinimum audit log level. Setting to warn keeps denied events while suppressing successful ones.

otlp

providers: audit: default: source: otlp config: endpoint: audit-collector:4317 protocol: grpc serviceName: gestaltd insecure: false headers: Authorization: Bearer ${AUDIT_OTLP_TOKEN} resourceAttributes: log.stream: audit

Exports audit records over OTLP without changing where application logs, metrics, or traces go. Use this when you want audit records in a dedicated compliance pipeline while keeping the rest of gestaltd on stdout or a different OTLP collector.

FieldDefaultDescription
endpointSDK defaultOTLP collector address.
protocolgrpcTransport protocol: grpc or http.
insecurefalseSkip TLS verification when true.
serviceNamegestaltdAttached to exported audit logs.
headersExtra headers on every export request.
resourceAttributesExtra key-value pairs attached to audit log exports.

noop

providers: audit: default: source: noop

Disables audit output explicitly. providers.audit.<name>.config is not allowed when providers.audit.<name>.source is noop.

apps

Each entry in the apps map is a named app backed by an executable provider package. The provider package is the source of truth for operations and transport details. Server config selects the provider, passes provider-specific config, and declares connection policy. The HTTP API exposes apps at /api/v1/apps, while the CLI uses the singular app command.

apps: github: displayName: GitHub description: Public GitHub operations iconFile: ./icons/github.svg authorizationPolicy: support_staff ui: path: /github bundle: github_console source: package: github.com/valon-technologies/gestalt-providers/app/github version: 0.0.1-alpha.1 surfaces: openapi: baseUrl: https://api.github.com indexeddb: provider: main db: github_app objectStores: - tasks - snapshots connections: default: mode: subject auth: type: oauth2 allowedOperations: repos.get: alias: get_repo allowedRoles: - admin

App Fields

FieldDescription
displayNameHuman-readable name shown in the UI and API.
descriptionShort description of the app.
iconFileSVG icon path, resolved relative to the config directory.
authorizationPolicyOptional shared subject access policy from authorization.policies. When set, host-side operation filtering and execution checks use the resolved role for this app, and the built-in admin UI / admin API can manage app-scoped dynamic grants for additional members. Static policy members still take precedence.
uiOptional app-backed mounted UI binding. Use ui.path plus optional ui.bundle. ui.bundle names a providers.ui entry; omit it to mount the app manifest’s owned spec.ui.
sourceRequired. The backing provider source. See source shape below.
source.authOptional metadata-fetch auth for package repositories, source.url, source.githubRelease, source.git, or local provider-release.yaml metadata.
authOptional app route auth reference. Use auth.provider to reference either the reserved server alias or a named entry from providers.authentication. Gestalt enforces this on app HTTP operation routes (/api/v1/apps/{name}/operations and /api/v1/{app}/{operation}) and on app-backed mounted UIs, including browser-login flows whose next path resolves into that app’s mount. Standalone providers.ui bundles, the built-in admin UI/admin API, and /mcp still use the server-wide auth provider in this phase.
configProvider-specific configuration passed into the provider package.
securitySchemesOptional hosted HTTP security scheme overrides merged over the app manifest’s spec.securitySchemes. Use this for deployment-specific secrets or for deployment-local hosted HTTP bindings.
httpOptional hosted HTTP binding overrides merged over the app manifest’s spec.http. Use this only when a deployment intentionally changes the route path, request body, security scheme, target operation, or acknowledgment response.
surfacesTyped host-side overrides for manifest-declared surfaces, such as surfaces.openapi.baseUrl or surfaces.graphql.url.
connectionsNamed connection bindings. A binding usually uses ref to attach a top-level connection to the local app connection name; simple single-use none connections may be declared inline. See connections below.
allowedOperationsAllowlist with optional alias, description, and allowedRoles overrides per operation.
dev.attach.allowedRolesRoles from this app’s authorizationPolicy that may create private remote provider-dev attachments for this app. This is denied when omitted, empty, or when the app has no authorizationPolicy.
runtimeOptional runtime placement for executable apps. Supports hosted runtime fields such as provider, template, image, imagePullAuth, and metadata.
egress.allowedHostsOutbound host allowlist. For executable apps, this is only a complete boundary when a supported sandboxed runtime is active.
indexeddbOptional app IndexedDB config. indexeddb.provider selects which named providers.indexeddb entry backs the app’s single IndexedDB SDK socket; when omitted, the app inherits the selected/default host IndexedDB. indexeddb.db overrides the app-visible database name and defaults to the app key. Supported providers derive app scoping from that db name using provider-native namespace config; Gestalt does not rewrite object-store names at the SDK transport layer. indexeddb.objectStores optionally allowlists the logical object stores the app may use. Apps then call new IndexedDB() (or the equivalent SDK helper) against GESTALT_INDEXEDDB_SOCKET; they do not choose arbitrary schemas or DSNs at runtime.
s3Optional list of named providers.s3 bindings exposed to the executable app through the SDK transport. A single binding also populates the default S3 SDK env var for SDK clients.

S3 bindings are exposed as GESTALT_S3_SOCKET_<NAME>. When exactly one S3 binding is configured, Gestalt also sets GESTALT_S3_SOCKET.

apps.*.source

Every app uses a source block to declare its backing provider.

For indexed providers, use a package source:

source: package: github.com/valon-technologies/gestalt-providers/app/github version: ^1.2.0

Add repo when the package comes from a non-default provider repository, or when the package address exists in more than one repository:

source: repo: acme package: github.com/acme/gestalt-providers/app/support version: ^1.3.0

gestaltd provider add writes package sources by default. See Provider packages for repository, add, list, remove, and upgrade behavior.

For commit-pinned first-party development, use a git source. ref must be a full commit SHA. materialization: source fetches and prepares from the git source when prepared artifacts are missing:

source: git: repo: https://github.com/acme/gestalt-providers.git ref: 60fa7e0521eeedea8b28abdd9b43284e9ea246e2 path: apps/github/manifest.yaml materialization: source

To consume prebuilt snapshots, set artifactRepository and an explicit materialization: snapshot. Snapshot materialization never falls back to git or provider indexes:

providerSnapshotRepositories: acme: url: https://storage.googleapis.com/acme-gestalt-provider-snapshots apps: github: source: git: repo: https://github.com/acme/gestalt-providers.git ref: 60fa7e0521eeedea8b28abdd9b43284e9ea246e2 path: apps/github/manifest.yaml artifactRepository: acme materialization: snapshot

For exact published releases, point at a provider-release.yaml URL:

source: https://github.com/valon-technologies/gestalt-providers/releases/download/app/github/v1.2.3/provider-release.yaml

Canonical metadata-source auth lives under source.auth. For direct metadata URLs:

source: url: https://github.com/valon-technologies/gestalt-providers/releases/download/app/github/v1.2.3/provider-release.yaml auth: token: ${APP_RELEASE_TOKEN}

App route auth overrides are declared separately:

apiVersion: gestaltd.config/v6 apps: slack_bot: source: ./dist/provider-release.yaml auth: provider: server

This field changes runtime authentication for app HTTP operation routes and app-backed mounted UIs. Browser login uses the app’s route-auth provider when its next path resolves into that app’s mounted UI. Standalone providers.ui bundles, the built-in admin UI/admin API, integration OAuth helper routes, and /mcp still use the server-wide auth provider in this phase.

Do not put release-download credentials in apps.<name>.auth. That field selects route authentication for the mounted app. Use source.auth for fetching private provider-release.yaml metadata or private GitHub release assets.

For published providers, source can point at a provider-release.yaml metadata URL directly, or it can describe a GitHub release logically:

source: githubRelease: repo: valon-technologies/toolshed tag: apps/github/v1.2.3 asset: provider-release.yaml

For local development, source can point directly at a provider manifest path instead.

For local development with a source provider package:

source: ./manifest.yaml

When a remote gestaltd instance is running the provider-dev endpoints, authenticated developers can attach a local implementation for configured apps that are registered on the remote server and that they are authorized to access with gestaltd serve --remote. A path-only attach matches the local manifest source to the remote configured app and preserves the remote app config; layered --config files are only needed for explicit local config overrides or multi-app attach sessions.

Remote provider attach is disabled by default on servers. Enable it explicitly on shared remotes:

server: dev: attachmentState: indexeddb authorization: policies: provider_devs: default: deny members: - subjectID: user:usr_123 role: developer apps: slack: authorizationPolicy: provider_devs dev: attach: allowedRoles: - developer

The attach transport is private to the authenticated owner. The create response includes an attachId for addressing and a one-time dispatcher secret used only by the local CLI for poll/complete traffic. Owner diagnostic list/get routes return redacted attachment metadata and never return dispatcher secrets, runtime environment, host-service env, app config, access tokens, or browser binding URLs.

attachmentState: indexeddb stores attachment metadata and queued provider/UI calls in the selected host IndexedDB, so any gestaltd replica can accept attach creation, CLI poll/complete traffic, provider invocations, and provider-owned UI requests. Process-local attachment state is not exposed as a server config mode because it is unsafe for load-balanced or multi-replica servers.

Interactive attach creation uses a short-lived browser approval page plus a verification code shown in the initiating terminal. The approving browser session must satisfy the runtime grant. For non-interactive/API-token attach, the caller must satisfy both the runtime grant and a token action grant. The runtime grant comes from apps.<name>.dev.attach.allowedRoles on a app with an authorizationPolicy, or from a runtime authorizer that explicitly grants the same action. API tokens must carry permissions[].actions: ["provider_dev.attach"] for every attached app; normal provider scopes and operation permissions do not grant attach, and the attach action does not grant operation invocation.

Source-backed executable apps can declare a local source command with top-level run. Gestalt runs that argv from the manifest directory when serving local source. Use object-form build.command with entrypoint.artifactPath when the same source package also needs a compiled release artifact.

kind: app source: github.com/acme/apps/my-app version: 1.0.0 build: command: - ./scripts/build-provider.sh inputs: - scripts - src - package.json run: - npm - run - dev:provider entrypoint: artifactPath: .gestalt/build/provider spec: connections: default: auth: type: none
FieldDescription
sourcePackage source, local provider manifest path, published provider-release.yaml metadata URL, source.url, or source.githubRelease for a GitHub-hosted release asset.
source.authOptional metadata-fetch auth for package repositories, direct metadata URLs, source.githubRelease, and local provider-release.yaml metadata.
authOptional app route auth reference. Use auth.provider to reference either server or a named providers.authentication entry. Gestalt enforces this on app HTTP operation routes and on app-backed mounted UIs. Standalone providers.ui bundles, the built-in admin UI/admin API, and /mcp still use the server-wide auth provider in this phase.
envExtra environment variables passed to the provider process.
egress.allowedHostsOutbound host allowlist. For executable apps, this is only a complete boundary when a supported sandboxed runtime is active.

connections

Top-level connections declare reusable upstream credential definitions. Apps and agent providers bind those definitions under their local apps.<name>.connections or providers.agent.<name>.connections maps with ref. The top-level connection ID is the stable credential namespace used by the external-credentials provider; the local binding name is only how that provider refers to the connection at runtime.

connections: slack-workspace: mode: subject auth: type: oauth2 authorizationUrl: https://accounts.example.com/oauth/authorize tokenUrl: https://accounts.example.com/oauth/token clientId: ${CLIENT_ID} clientSecret: ${CLIENT_SECRET} params: tenant: required: true description: Tenant identifier apps: slack: source: ./apps/slack/manifest.yaml connections: default: ref: slack-workspace

Agent providers use the same binding model. This lets an agent backend resolve a provider-owned model connection through AgentHost.ResolveConnection instead of receiving secrets in static config:

connections: vertex-kimi: mode: subject auth: type: bearer params: baseUrl: https://example.vertexai.goog/v1 providers: agent: simple: source: ./providers/agent/simple/manifest.yaml connections: model: ref: vertex-kimi config: defaultModel: vertex/kimi-k2-6 modelOptions: vertex: connection: model

For automation, provision the credential onto a service account, then execute the workflow or agent turn with runAs. Named slots such as Slack’s bot connection remain normal subject-owned connections.

connections: gmail-service-mailbox: mode: subject auth: type: oauth2 tokenUrl: https://oauth2.googleapis.com/token clientId: ${GOOGLE_OAUTH_CLIENT_ID} clientSecret: ${GOOGLE_OAUTH_CLIENT_SECRET} apps: gmail: source: ./apps/gmail/manifest.yaml connections: service: ref: gmail-service-mailbox

If the target provider deliberately manages its own upstream credentials, such as by minting a short-lived Google domain-wide-delegation token from provider config, declare the nested grant with credentialMode: none instead. That skips Gestalt credential resolution for the call and leaves token minting and policy enforcement to the provider.

The effective connection is resolved from the most specific declaration first. A manifest surface can name a connection with spec.surfaces.<surface>.connection, and declarative REST operations can name a connection directly. If no surface or operation connection is named, Gestalt uses defaultConnection, then a connection literally named default, then the only named connection when there is exactly one.

Connection mode determines how credentials are managed: none requires no credentials, and subject stores credentials for the caller’s effective subject. For humans that is typically user:<id>; for non-human API token callers it can be service_account:<id> or another canonical non-system subject ID. Runtime surfaces can also supply system subjects such as system:http_binding:<app>:<binding> as the effective credential owner.

Binding entries that use ref may set only binding metadata such as displayName and credentialRefresh; they cannot override mode, auth, or params. Top-level connections cannot set ref. Inline connection bindings are available for simple single-use none cases; subject-scoped connections that collect or exchange credentials must be declared at the top level and referenced by ref.

Credential Refresh

Connections can opt into proactive external credential maintenance with credentialRefresh. Omitting this block keeps the connection on the default on-demand refresh path.

connections: google-workspace: mode: subject auth: type: oauth2 tokenUrl: https://oauth2.googleapis.com/token clientId: ${GOOGLE_CLIENT_ID} clientSecret: ${GOOGLE_CLIENT_SECRET} credentialRefresh: refreshInterval: 15m refreshBeforeExpiry: 30m
FieldDescription
refreshIntervalHow often a supporting external credentials provider should scan stored credentials for this connection. Must be a positive duration.
refreshBeforeExpiryHow far before expiry credentials should be refreshed. Must be a positive duration.

Gestalt treats this as connection metadata. It resolves manifest defaults, deployment overrides, and ref bindings, then passes the resolved connection metadata to the configured external credentials provider. The external credentials provider decides whether it supports proactive maintenance for the resolved auth shape and owns the refresh behavior.

Authentication Shapes

Every connection auth block uses the same shape. type selects the auth strategy: oauth2, manual, bearer, none, or mcp_oauth.

FieldDefaultDescription
typeAuthentication strategy: oauth2, manual, bearer, none, or mcp_oauth.
grantTypeReserved. Declarative client_credentials and refresh_token provisioning is not supported; provision credentials onto a subject through the external credentials provider instead.
refreshTokenReserved for provider/runtime credential transport. Do not configure deployer-owned refresh tokens in Gestalt config.
authorizationUrlOAuth2 authorization endpoint.
tokenUrlOAuth2 token endpoint.
clientIdOAuth2 client identifier.
clientSecretOAuth2 client secret.
redirectUrlderived from server.baseUrlOAuth2 callback URL.
clientAuthbodyHow credentials are sent to the token endpoint: body or header.
tokenExchangeformToken request encoding: form or json.
scopesOAuth2 scopes to request.
scopeParamscopeQuery parameter name for scopes. Set to user_scope for Slack and other providers that use a non-standard parameter.
scopeSeparatorspaceSeparator between scope values.
pkcefalseEnable Proof Key for Code Exchange.
authorizationParamsExtra parameters for the authorization request.
tokenParamsExtra parameters for the token request.
refreshParamsExtra parameters for the refresh request.
acceptHeaderAccept header for the token endpoint.
accessTokenPathJSON path to the access token in the token response.
tokenMetadataFields to extract from the token response and store as connection metadata.

mcp_oauth is an advanced mode for MCP-driven OAuth discovery. It delegates authorization URL and token URL resolution to the upstream MCP server.

Manual auth with custom credential fields

For manual-auth connections, credentials customizes the credential input form. Each entry defines a field the user must fill in, with an optional label, description, and help URL.

Single credential with a custom label:

connections: default: auth: type: manual credentials: - name: token label: API Access Token

Multiple credentials require authMapping to map each field to an HTTP header:

connections: default: auth: type: manual credentials: - name: api_key label: API Key - name: app_key label: Application Key authMapping: headers: DD-API-KEY: valueFrom: credentialFieldRef: name: api_key DD-APPLICATION-KEY: valueFrom: credentialFieldRef: name: app_key
FieldDescription
credentials[].nameUnique, lowercase-with-underscores identifier. Required when auth.type is manual.
credentials[].labelUI display label. Falls back to name when omitted.
credentials[].descriptionHint text below the label. Supports inline links via [text](url).
authMapping.headers.<HEADER>.valueSupplies a static deployer-managed value for a header.
authMapping.headers.<HEADER>.valueFrom.credentialFieldRef.nameReads a header value from the named manual credential field.
authMapping.basic.username / authMapping.basic.passwordBuild the Basic authentication username/password from either a static value or valueFrom.credentialFieldRef.name.

For Basic authentication providers that should collect only an API key while keeping the organization ID static, declare just the user-facing field and map the rest explicitly:

connections: default: auth: type: manual credentials: - name: api_key label: API Key authMapping: basic: username: value: ${MODERN_TREASURY_ORG_ID} password: valueFrom: credentialFieldRef: name: api_key

Post-connect discovery

Post-connect discovery lets a connection fetch a list of resources after the user authenticates, populating a connection parameter from the user’s selection.

connections: default: mode: subject auth: type: oauth2 authorizationUrl: https://accounts.example.com/oauth/authorize tokenUrl: https://accounts.example.com/oauth/token params: workspace_id: required: true description: Workspace chosen after connect from: discovery discovery: url: https://api.example.com/workspaces idPath: id namePath: name metadata: workspace_id: id
FieldDescription
discovery.urlURL to fetch the list of resources after connect.
discovery.idPathJSON path to the resource identifier.
discovery.namePathJSON path to the resource display name.
discovery.metadataMaps connection metadata keys to JSON paths in the discovery response.

server.egress

server.egress sets the server-wide default for outbound network access. Per-provider egress.allowedHosts lists declare which hosts each provider may reach. For executable apps, that declaration is only a complete enforcement boundary when the deployment is using a supported sandboxed runtime.

server: egress: defaultAction: deny apps: github: egress: allowedHosts: - api.github.com - "*.github.com" my-app: source: ./apps/my-app/manifest.yaml egress: allowedHosts: - external-api.com
FieldDescription
defaultActionallow (default) or deny. Applies when a provider has no egress.allowedHosts.

When egress.allowedHosts is set on a provider, Gestalt applies that allowlist to host-mediated outbound checks using exact matches and *.suffix wildcards. When egress.allowedHosts is empty, defaultAction decides: allow permits outbound requests, deny blocks them. Provider-level allowedHosts is no longer accepted; use egress.allowedHosts. For executable apps, those rules are only a complete security boundary when the app is running inside a supported sandboxed runtime rather than relying on host-specific OS wrappers alone.

providers.ui

providers: ui: console: source: package: github.com/valon-technologies/gestalt-providers/ui/default version: 0.0.1 path: /console config: brandName: Acme

providers.ui is a map of ui bundles. Entries may mount directly by setting path, or they may act as named UI bundles that apps.<name>.ui.bundle binds to apps.<name>.ui.path. When a app manifest declares spec.ui, apps.<name>.ui.bundle may be omitted entirely and Gestalt synthesizes the mounted UI entry from the app-owned reference. /admin remains available regardless. A selected server.admin.ui bundle, or the root-mounted UI bundle when it contains admin/index.html, may also supply the static admin shell while Gestalt keeps the existing /admin auth and API semantics. Omit providers.ui entirely to run headless with no public UI bundles.

App-backed mounted UIs inherit the owning app’s dynamic authorization grants when that app sets authorizationPolicy. Direct providers.ui.<name>.authorizationPolicy bindings remain static-only.

When authorizationPolicy is set on a mounted UI, the referenced UI manifest must declare spec.routes with non-empty allowedRoles, and at least one route must cover /.

FieldDescription
pathDirect mount prefix for this UI when not bound through apps.<name>.ui.path.
authorizationPolicyDirect policy binding for this UI when not bound through apps.<name>.ui.path.
sourcePackage source, local UI manifest path, published provider-release.yaml metadata URL, source.url, or source.githubRelease for a GitHub-hosted release asset.
source.authOptional inline source auth for package repositories, remote release sources, direct metadata URLs, and source.githubRelease.
configOptional UI-provider config validated against the bundle schema when present.

Validation rules

Structural validation runs at config load time (lock, sync, validate, serve). Runtime validation runs only at serve time.

Structural

Each app must set source to exactly one loading mode: package source, local manifest path, published provider-release.yaml metadata URL, source.url, or source.githubRelease.

Host-provider entries use the same ProviderEntry shape. When providers.authentication.<name>, providers.cache.<name>, providers.indexeddb.<name>, or providers.s3.<name> is set, source must be present. providers.authentication.<name>.config, providers.cache.<name>.config, providers.indexeddb.<name>.config, and providers.s3.<name>.config are only allowed when their corresponding source is set.

Top-level connections cannot set ref or exposure. Connection bindings that set ref cannot also set mode, auth, or params. Subject-scoped inline connections that collect or exchange credentials are not supported; declare a top-level connection and reference it with ref. All connections in a app must share the same mode.

Each providers.ui.<name> entry must set source to exactly one loading mode: package source, local UI manifest path, published provider-release.yaml metadata URL, source.url, or source.githubRelease. providers.ui.<name>.config is only allowed when providers.ui.<name>.source is set.

Each apps.<name> entry may set ui.path to publish a app-backed UI. When ui.bundle is set, it must reference an existing UI bundle. When ui.bundle is omitted, the app manifest must declare spec.ui. A given UI may be bound by at most one app entry. ui.path must use the same path rules as a directly mounted UI.

When server.admin.ui is set, it must reference an existing providers.ui.<name> entry. Runtime startup then requires that entry’s prepared asset root to include admin/index.html.

Each name in apps.<name>.s3 must reference an existing enabled providers.s3.<name> entry.

Mounted UI bundles are served on the public listener only. The built-in admin UI remains at /admin.

server.egress.defaultAction must be allow or deny.

authorization.policies.<name>.default must be allow or deny when set. Each policy member must set a non-empty subjectID and a non-empty role.

Server listener ports must be greater than zero. When server.management is configured, it must differ from server.public.

Runtime

server.providers.indexeddb, providers.indexeddb, and server.encryptionKey are required at serve time. providers.authentication is optional.