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
nulldeletes 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.comOverride:
server:
management:
port: 9090
apps:
github:
egress:
allowedHosts:
- api.github.com
iconFile: ./icons/github.svg
displayName: nullRun:
gestaltd validate --config ./base.yaml --config ./overrides/local.yamlThe 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.yamlUse 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.
| Field | Description |
|---|---|
providerRepositories.<name>.url | HTTP(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| Field | Description |
|---|---|
providerSnapshotRepositories.<name>.url | HTTP(S) root containing deterministic snapshot provider-release.yaml files and archives. |
providerSnapshotRepositories.<name>.gestaltRef | Optional 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: viewerpublic.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.
| Field | Default | Description |
|---|---|---|
public.host | 0.0.0.0 | Bind address for the public listener. |
public.port | 8080 | Port for the public listener. |
management.host | Bind address for the management listener. | |
management.port | Port for the management listener. Required when management.host is set. | |
management.baseUrl | Browser-facing absolute URL for the management listener. Required for protected built-in /admin on split public/management deployments. | |
baseUrl | Public origin URL used for OAuth callbacks and admin UI links. | |
encryptionKey | Required. Root encryption key. Typically a structured secret ref. | |
apiTokenTtl | 30d | Lifetime of newly issued API tokens. Go durations or day suffixes (30d). |
artifactsDir | config directory | Directory for prepared artifacts produced by gestaltd sync --locked. |
agent.defaultToolNarrowingThreshold | 200 | Visible 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.telemetry | Optional 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.attachmentState | Enables 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.defaultProvider | Default runtime-provider selector for apps and providers that opt into runtime. This does not move anything into hosted runtimes by itself. | |
runtime.relayBaseUrl | server.baseUrl | Absolute HTTP(S) URL that hosted runtimes use for Gestalt host-service relay and egress proxy callbacks. |
admin.authorizationPolicy | Shared 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.ui | Optional 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.policies | Shared subject access policies resolved by canonical subjectID. | |
authorization.models | Static authorization model fragments keyed by model name. Each fragment defines resourceTypes, relation subjectTypes or allowedTargets, and actions. | |
authorization.relationships | Static relationship tuples with subject, relation, and resource fields. | |
authorization.resourceTypes.<type>.dynamic.allowAdditionalRelationships | false | Allows 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: portForwardserver.runtime.defaultProvider supplies the default runtime name for
apps, agent providers, or workflow providers that set runtime without an explicit
runtime.provider.
| Field | Description |
|---|---|
providers.<name>.driver | Optional built-in runtime selector. The only built-in runtime is local. |
providers.<name>.source | Package source, local manifest path, metadata URL, source.url, or source.githubRelease for an installable kind: runtime provider such as Kubernetes. |
providers.<name>.default | Marks the runtime as the default selection when server.runtime.defaultProvider is omitted. Only one runtime may be default. |
providers.<name>.config | Runtime-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| Field | Description |
|---|---|
namespace | Kubernetes namespace where runtime session Pods are created. |
container | Container name that contains the app executable and socket bridge tooling. |
appPort | TCP port exposed by the hosted app gRPC server inside the Pod. |
connectionMode | How gestaltd connects to the hosted app: portForward, podIP, or serviceDNS. |
sessionReadyTimeout | How long to wait for the runtime Pod to become ready. |
execTimeout | Timeout for Kubernetes exec calls used to launch or stop the app process. |
cleanupTimeout | Timeout 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: 10000Two 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/.
| Field | Description |
|---|---|
default | Marks this named authentication entry as the default selection when server.providers.authentication is omitted. |
source | Package source, local provider manifest path, published provider-release.yaml metadata URL, source.url, or source.githubRelease for a GitHub-hosted release asset. |
source.auth | Optional metadata-fetch auth for package repositories, direct metadata URLs, local release metadata, and source.githubRelease. |
config | Provider-specific config passed into the authentication provider. |
env | Extra environment variables passed to the provider process. |
egress.allowedHosts | Outbound 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:
| Provider | Package path | Config fields |
|---|---|---|
| RelationalDB | github.com/valon-technologies/gestalt-providers/indexeddb/relationaldb | dsn |
| DynamoDB | github.com/valon-technologies/gestalt-providers/indexeddb/dynamodb | table, region, optional endpoint |
| MongoDB | github.com/valon-technologies/gestalt-providers/indexeddb/mongodb | uri, 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: mainAuthorization 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.
| Field | Description |
|---|---|
default | Marks this named authorization entry as the default selection when server.providers.authorization is omitted. |
source | Package source, local provider manifest path, published provider-release.yaml metadata URL, source.url, or source.githubRelease for a GitHub-hosted release asset. |
source.auth | Optional inline source auth for package repositories, remote release sources, direct metadata URLs, and source.githubRelease. |
config | Provider-specific config passed into the authorization provider. |
env | Extra environment variables passed to the provider process. |
egress.allowedHosts | Outbound 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_itemsproviders.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| Field | Description |
|---|---|
default | Marks this named workflow entry as the default selection when a config-managed workflow object or API request omits provider. |
source | Package source, local provider manifest path, published provider-release.yaml metadata URL, source.url, or source.githubRelease for a GitHub-hosted release asset. |
source.auth | Optional inline source auth for package repositories, remote release sources, direct metadata URLs, and source.githubRelease. |
config | Provider-specific config passed into the workflow provider. |
indexeddb | Optional host IndexedDB binding for the workflow provider, including provider, optional db, and optional objectStores. |
env | Extra environment variables passed to the provider process. |
runtime | Optional 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.allowedHosts | Outbound 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.5Agent 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.
| Field | Description |
|---|---|
default | Marks this named agent entry as the default selection when a session request omits provider. |
source | Package source, local provider manifest path, published provider-release.yaml metadata URL, source.url, or source.githubRelease for a GitHub-hosted release asset. |
source.auth | Optional inline source auth for package repositories, remote release sources, direct metadata URLs, and source.githubRelease. |
indexeddb | Optional 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. |
config | Provider-specific config passed into the agent provider. |
env | Extra environment variables passed to the provider process. |
defaultHarness | Optional default local harness name used by gestalt agent and gestalt agent doctor when --harness is omitted. |
harnesses | Optional named local harness configs for gestalt agent. Each harness can define command, args, env, workingDirectory, requiredCommands, and install guidance. |
runtime | Optional 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.allowedHosts | Outbound 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: falseworkflows.schedules.*
| Field | Description |
|---|---|
provider | Optional named providers.workflow entry. When omitted, Gestalt uses the sole/default workflow provider. |
target.steps | Required ordered workflow steps. Each step has id and exactly one of app or agent. |
target.steps[].inputs | Optional named values available to the step. Values use the same source shape as app input values. |
target.steps[].app.name | Target app key from apps. Required for app steps. |
target.steps[].app.operation | Target operation ID. Required for app steps. |
target.steps[].app.connection | Optional target connection name. |
target.steps[].app.instance | Optional target instance name. |
target.steps[].app.input | Optional workflow value that must resolve to a JSON object. Use literal for static params. |
target.steps[].agent | Optional agent turn using provider, model, sessionKey, prompt, messages, tools, responseSchema, and modelOptions. |
target.steps[].when | Optional guard with value and scalar equals; value.stepOutput.stepId may reference only earlier steps. |
runAs.subject.id | Optional 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.kind | Optional 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.authSource | Optional metadata copied into the runtime principal and run-as audit fields. |
invokes | Optional 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. |
cron | Required. Five-field cron expression. |
timezone | IANA timezone name. Defaults to UTC. |
paused | When true, Gestalt reconciles the schedule in a paused state. |
workflows.eventTriggers.*
| Field | Description |
|---|---|
provider | Optional named providers.workflow entry. When omitted, Gestalt uses the sole/default workflow provider. |
target.steps | Required ordered workflow steps. Each step has id and exactly one of app or agent. |
target.steps[].app | App step call fields: name, operation, optional connection, optional instance, and optional workflow-value input. |
target.steps[].agent | Agent step turn fields: provider, model, sessionKey, prompt, messages, tools, responseSchema, and modelOptions. |
target.steps[].when | Optional guard with value and scalar equals; value.stepOutput.stepId may reference only earlier steps. |
runAs.subject.id | Optional service-account subject ID used as the runtime principal and credential subject. See runAs. |
runAs.subject.kind | Optional 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.authSource | Optional metadata copied into the runtime principal and run-as audit fields. |
invokes | Optional 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.type | Required. Event type matcher. |
match.source | Optional event source matcher. |
match.subject | Optional event subject matcher. |
paused | When 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:
- sessionFirst-party cache providers are published at
github.com/valon-technologies/gestalt-providers/cache/<name>.
| Field | Description |
|---|---|
source | Package source, local provider manifest path, published provider-release.yaml metadata URL, source.url, or source.githubRelease for a GitHub-hosted release asset. |
source.auth | Optional inline source auth for package repositories, remote release sources, direct metadata URLs, and source.githubRelease. |
config | Arbitrary provider-specific config passed through to the cache provider. |
env | Extra environment variables passed to the provider process. |
egress.allowedHosts | Outbound 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:
- assetsThe 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-keyConfigure 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| Source | Config fields | Purpose |
|---|---|---|
env | prefix | Reads secrets from environment variables. Default when providers.secrets is omitted. |
file | dir | Reads 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| Provider | Package path | Config fields |
|---|---|---|
| Google Secret Manager | github.com/valon-technologies/gestalt-providers/secrets/google | project (required), version |
| AWS Secrets Manager | github.com/valon-technologies/gestalt-providers/secrets/aws | region (required), versionStage, endpoint |
| HashiCorp Vault | github.com/valon-technologies/gestalt-providers/secrets/vault | address (required), token (required), mountPath, namespace |
| Azure Key Vault | github.com/valon-technologies/gestalt-providers/secrets/azure | vaultUrl (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:
| Provider | Authentication model |
|---|---|
env | Reads from process environment. No external auth needed. |
file | Reads from local filesystem. No external auth needed. |
| Google Secret Manager | Uses Application Default Credentials . In production, attach a service account or use workload identity. Locally, run gcloud auth application-default login. |
| AWS Secrets Manager | Uses 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 Vault | Requires explicit address and token in config. Inject the token via ${VAULT_TOKEN} or ${VAULT_TOKEN_FILE}. |
| Azure Key Vault | Uses 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: gestaltdOutputs 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.
| Field | Default | Description |
|---|---|---|
format | text | Log format: text or json. |
level | info | Minimum log level: debug, info, warn, or error. |
serviceName | gestaltd | Attached to locally exposed metrics. |
resourceAttributes | Extra key-value pairs attached to local metrics. | |
metrics.prometheus.enabled | true | Toggle 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: infoExports 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.
| Field | Default | Description |
|---|---|---|
endpoint | SDK default | OTLP collector address. |
protocol | grpc | Transport protocol: grpc or http. |
insecure | false | Skip TLS verification when true. |
headers | Extra headers on every export request. | |
serviceName | gestaltd | OpenTelemetry service name. |
resourceAttributes | Arbitrary key-value pairs as OTel resource attributes. | |
traces.samplingRatio | 1.0 | Fraction of traces sampled, from 0.0 to 1.0. |
metrics.interval | 60s | OTLP metric export interval. |
metrics.prometheus.enabled | true | Toggle the local /metrics scrape endpoint. |
logs.exporter | otlp | Where logs go: otlp or stdout. |
logs.level | info | Minimum log level. |
logs.format | text | Log 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: infonoop
providers:
telemetry:
default:
source: noopDisables 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
| Layer | Span name | Key attributes |
|---|---|---|
| HTTP | gestaltd: {method} {route} | Standard HTTP semantic conventions plus resolved gestaltd.* request-surface attributes |
| Broker | broker.invoke | gestalt.provider, gestalt.operation, gestalt.subject_id, gestalt.connection_mode |
| gRPC app and host service traffic | Per-RPC spans | Standard 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: inheritThis 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: jsonWrites audit records to standard output even if the main telemetry pipeline is using a different provider.
| Field | Default | Description |
|---|---|---|
format | text | Output format: text or json. |
level | info | Minimum 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: auditExports 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.
| Field | Default | Description |
|---|---|---|
endpoint | SDK default | OTLP collector address. |
protocol | grpc | Transport protocol: grpc or http. |
insecure | false | Skip TLS verification when true. |
serviceName | gestaltd | Attached to exported audit logs. |
headers | Extra headers on every export request. | |
resourceAttributes | Extra key-value pairs attached to audit log exports. |
noop
providers:
audit:
default:
source: noopDisables 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:
- adminApp Fields
| Field | Description |
|---|---|
displayName | Human-readable name shown in the UI and API. |
description | Short description of the app. |
iconFile | SVG icon path, resolved relative to the config directory. |
authorizationPolicy | Optional 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. |
ui | Optional 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. |
source | Required. The backing provider source. See source shape below. |
source.auth | Optional metadata-fetch auth for package repositories, source.url, source.githubRelease, source.git, or local provider-release.yaml metadata. |
auth | Optional 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. |
config | Provider-specific configuration passed into the provider package. |
securitySchemes | Optional 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. |
http | Optional 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. |
surfaces | Typed host-side overrides for manifest-declared surfaces, such as surfaces.openapi.baseUrl or surfaces.graphql.url. |
connections | Named 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. |
allowedOperations | Allowlist with optional alias, description, and allowedRoles overrides per operation. |
dev.attach.allowedRoles | Roles 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. |
runtime | Optional runtime placement for executable apps. Supports hosted runtime fields such as provider, template, image, imagePullAuth, and metadata. |
egress.allowedHosts | Outbound host allowlist. For executable apps, this is only a complete boundary when a supported sandboxed runtime is active. |
indexeddb | Optional 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. |
s3 | Optional 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.0Add 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.0gestaltd 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: sourceTo 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: snapshotFor 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.yamlCanonical 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: serverThis 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.yamlFor local development, source can point directly at a provider manifest path instead.
For local development with a source provider package:
source: ./manifest.yamlWhen 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:
- developerThe 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| Field | Description |
|---|---|
source | Package source, local provider manifest path, published provider-release.yaml metadata URL, source.url, or source.githubRelease for a GitHub-hosted release asset. |
source.auth | Optional metadata-fetch auth for package repositories, direct metadata URLs, source.githubRelease, and local provider-release.yaml metadata. |
auth | Optional 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. |
env | Extra environment variables passed to the provider process. |
egress.allowedHosts | Outbound 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-workspaceAgent 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: modelFor 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-mailboxIf 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| Field | Description |
|---|---|
refreshInterval | How often a supporting external credentials provider should scan stored credentials for this connection. Must be a positive duration. |
refreshBeforeExpiry | How 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.
| Field | Default | Description |
|---|---|---|
type | Authentication strategy: oauth2, manual, bearer, none, or mcp_oauth. | |
grantType | Reserved. Declarative client_credentials and refresh_token provisioning is not supported; provision credentials onto a subject through the external credentials provider instead. | |
refreshToken | Reserved for provider/runtime credential transport. Do not configure deployer-owned refresh tokens in Gestalt config. | |
authorizationUrl | OAuth2 authorization endpoint. | |
tokenUrl | OAuth2 token endpoint. | |
clientId | OAuth2 client identifier. | |
clientSecret | OAuth2 client secret. | |
redirectUrl | derived from server.baseUrl | OAuth2 callback URL. |
clientAuth | body | How credentials are sent to the token endpoint: body or header. |
tokenExchange | form | Token request encoding: form or json. |
scopes | OAuth2 scopes to request. | |
scopeParam | scope | Query parameter name for scopes. Set to user_scope for Slack and other providers that use a non-standard parameter. |
scopeSeparator | space | Separator between scope values. |
pkce | false | Enable Proof Key for Code Exchange. |
authorizationParams | Extra parameters for the authorization request. | |
tokenParams | Extra parameters for the token request. | |
refreshParams | Extra parameters for the refresh request. | |
acceptHeader | Accept header for the token endpoint. | |
accessTokenPath | JSON path to the access token in the token response. | |
tokenMetadata | Fields 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 TokenMultiple 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| Field | Description |
|---|---|
credentials[].name | Unique, lowercase-with-underscores identifier. Required when auth.type is manual. |
credentials[].label | UI display label. Falls back to name when omitted. |
credentials[].description | Hint text below the label. Supports inline links via [text](url). |
authMapping.headers.<HEADER>.value | Supplies a static deployer-managed value for a header. |
authMapping.headers.<HEADER>.valueFrom.credentialFieldRef.name | Reads a header value from the named manual credential field. |
authMapping.basic.username / authMapping.basic.password | Build 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_keyPost-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| Field | Description |
|---|---|
discovery.url | URL to fetch the list of resources after connect. |
discovery.idPath | JSON path to the resource identifier. |
discovery.namePath | JSON path to the resource display name. |
discovery.metadata | Maps 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| Field | Description |
|---|---|
defaultAction | allow (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: Acmeproviders.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 /.
| Field | Description |
|---|---|
path | Direct mount prefix for this UI when not bound through apps.<name>.ui.path. |
authorizationPolicy | Direct policy binding for this UI when not bound through apps.<name>.ui.path. |
source | Package source, local UI manifest path, published provider-release.yaml metadata URL, source.url, or source.githubRelease for a GitHub-hosted release asset. |
source.auth | Optional inline source auth for package repositories, remote release sources, direct metadata URLs, and source.githubRelease. |
config | Optional 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.