Config File
Every gestaltd deployment reads one or more YAML config files. 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), providers (named authentication, secrets, telemetry, audit, UI,
indexeddb, authorization, workflow, cache, and s3 entries), workflows
(config-managed schedules and triggers), and plugins (executable
integrations and optional plugin-backed UI mounts):
server:
# listener, encryption, egress, host-provider selectors
authorization:
policies: {}
providers:
audit: {}
authentication: {}
authorization: {}
cache: {}
indexeddb: {}
s3: {}
secrets: {}
telemetry: {}
ui: {}
workflow: {}
workflows:
schedules: {}
eventTriggers: {}
plugins: {}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
plugins:
github:
source: ./plugins/github/manifest.yaml
egress:
allowedHosts:
- api.github.com
- uploads.github.comOverride:
server:
management:
port: 9090
plugins:
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 plugins.github.egress.allowedHosts, resolves iconFile relative to
./overrides/local.yaml, and deletes the inherited displayName.
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
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 init and consumed by serve --locked when no CLI override is supplied; it defaults to the config file’s directory. See Configuration for the full init/serve lifecycle.
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. Plugins opt into a policy explicitly through authorizationPolicy.
Dynamic grants are plugin-scoped, not policy-scoped. When a plugin sets authorizationPolicy, operators can manage additional dynamic members for that plugin from the built-in admin UI at /admin/?tab=members or through /admin/api/v1/authorization/.... Static policy members always win over dynamic grants, and Gestalt rejects dynamic writes for subjects that already have static authorization on that plugin. 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 canonical subjects, typically service_account:<id>, and authenticate with Gestalt API tokens whose stored owner is that subject. Grant plugin 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.
Plugins without authorizationPolicy are open to any authenticated subject. Bind a plugin 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 init artifacts. |
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. | |
providerDev.remoteAttach | false | Enables private remote provider-dev attach routes. Requires providerDev.attachmentState: processLocal. |
providerDev.attachmentState | Required when providerDev.remoteAttach is true. Use processLocal to acknowledge that remote attach v1 stores attachment state in this gestaltd process and therefore requires single-replica operation or sticky routing for provider-dev traffic. | |
runtime.defaultHostedProvider | Default runtime-provider selector for plugins that opt into execution.mode: hosted. This does not move plugins into hosted runtimes by itself. | |
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. |
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 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 executable plugins.
These backends are only used when a plugin opts into execution.mode: hosted.
If a plugin omits execution or sets execution.mode: local, 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
modal:
source:
path: ./vendor/gestalt-providers/runtime/modal/manifest.yaml
default: true
config:
app: gestalt-runtime
environment: mainserver.runtime.defaultHostedProvider supplies the default runtime name for
plugins that set execution.mode: hosted without an explicit
execution.runtime.provider. The legacy server.runtime.provider field is no longer accepted.
| Field | Description |
|---|---|
providers.<name>.driver | Optional built-in runtime selector. The only built-in runtime is local. |
providers.<name>.source | Source or provider-release reference for an installable kind: runtime provider such as Modal. |
providers.<name>.default | Marks the runtime as the default selection when server.runtime.defaultHostedProvider 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 Modal runtime provider
The Modal runtime provider creates hosted Linux sandboxes in a Modal app and connects to the plugin over a gRPC tunnel.
runtime:
providers:
modal:
source:
path: ./vendor/gestalt-providers/runtime/modal/manifest.yaml
config:
app: gestalt-runtime
environment: main
cpu: 2
memoryMiB: 4096
memoryLimitMiB: 6144
timeout: 30m
idleTimeout: 10m
cloud: aws
regions:
- us-east-1| Field | Description |
|---|---|
app | Required. Modal app name used to create or reuse sandboxes. |
environment | Optional Modal environment name. |
cpu | Optional CPU allocation for each sandbox. Must be non-negative. |
memoryMiB | Optional memory request in MiB. Must be non-negative. |
memoryLimitMiB | Optional memory limit in MiB. Must be non-negative. |
timeout | Optional absolute sandbox timeout. |
idleTimeout | Optional idle timeout before Modal reclaims the sandbox. |
cloud | Optional cloud selector passed through to Modal. |
regions | Optional region allowlist passed through to Modal. |
When a plugin uses the Modal runtime provider,
plugins.<name>.execution.runtime.image is required. Modal can still run
hosted plugins 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 and server.encryptionKey must be set, and the runtime
must report a compatible support profile. Without those prerequisites, Gestalt
will reject Modal for plugins that need relay-backed host services,
egress.allowedHosts, or a server-wide default-deny egress policy.
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:
plugins:
support:
source: https://artifacts.example.com/support/v0.1.0/provider-release.yaml
execution:
mode: hosted
runtime:
provider: modal
image: ghcr.io/example/support-plugin@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: https://artifacts.example.com/auth/oidc/v0.0.1-alpha.1/provider-release.yaml
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 | Either a local provider manifest path, a published provider-release.yaml metadata URL, source.url, or source.githubRelease for a GitHub-hosted release asset. |
source.auth | Optional metadata-fetch auth for release sources, including direct metadata URLs 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 plugins, 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: https://artifacts.example.com/indexeddb/relationaldb/v0.0.1-alpha.1/provider-release.yaml
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: https://artifacts.example.com/indexeddb/relationaldb/v0.0.1-alpha.1/provider-release.yaml
config:
dsn: ${DATABASE_URL}
authorization:
indexeddb:
source: https://artifacts.example.com/authorization/indexeddb/v0.0.1-alpha.1/provider-release.yaml
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 plugin/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 | Either a local provider manifest path, a published provider-release.yaml metadata URL, or source.githubRelease for a GitHub-hosted release asset. |
auth | Optional inline source auth for remote release sources, including 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 plugins, 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: https://artifacts.example.com/indexeddb/relationaldb/v0.0.1-alpha.1/provider-release.yaml
config:
dsn: ${DATABASE_URL}
workflow:
local:
source: https://artifacts.example.com/workflow/indexeddb/v0.0.1-alpha.1/provider-release.yaml
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
target:
plugin:
name: roadmap
operation: sync_items
eventTriggers:
item_updated:
provider: local
match:
type: roadmap.item.updated
target:
plugin:
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.
| Field | Description |
|---|---|
default | Marks this named workflow entry as the default selection when a config-managed workflow object or API request omits provider. |
source | Either a local provider manifest path, a published provider-release.yaml metadata URL, or source.githubRelease for a GitHub-hosted release asset. |
auth | Optional inline source auth for remote release sources, including 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. |
egress.allowedHosts | Outbound host allowlist. For executable plugins, 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 plugins that call the
host agent manager.
providers:
agent:
simple:
source: ./providers/agent/simple/manifest.yaml
default: true
indexeddb:
provider: default
db: simple_agent
execution:
mode: hosted
runtime:
provider: modal
image: ghcr.io/valon/gestalt-python-runtime:latest
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: anthropic/claude-sonnet-4-20250514Agent 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
execution.mode: hosted, using the same runtime-provider selection model as
executable plugins. When providers.agent.<name>.execution.mode is hosted,
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. Legacy providers.agent.<name>.runtime is no
longer accepted; use providers.agent.<name>.execution.runtime.
| Field | Description |
|---|---|
default | Marks this named agent entry as the default selection when a session request omits provider. |
source | Either a local provider manifest path, a published provider-release.yaml metadata URL, or source.githubRelease for a GitHub-hosted release asset. |
auth | Optional inline source auth for remote release sources, including 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 plugins, 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. |
execution | Optional hosted execution selection for this agent provider. execution.mode is local or hosted; execution.runtime supports provider, template, image, imagePullAuth, metadata, and pool. imagePullAuth.dockerConfigJson contains Docker config JSON used by runtime providers when pulling private OCI images. 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. A target is exactly one of target.plugin or
target.agent.
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:
plugin:
name: roadmap
operation: sync_items
connection: default
instance: tenant-a
input:
mode: incremental
paused: false
eventTriggers:
item_updated:
provider: local
match:
type: roadmap.item.updated
source: roadmap
target:
plugin:
name: roadmap
operation: sync_items
input:
reason: sync
paused: falseworkflows.schedules.*
| Field | Description |
|---|---|
provider | Optional named providers.workflow entry. When omitted, Gestalt uses the sole/default workflow provider. |
target.plugin.name | Target plugin key from plugins. Required when target.plugin is used. |
target.plugin.operation | Target operation ID. Required when target.plugin is used. |
target.plugin.connection | Optional target connection name. |
target.plugin.instance | Optional target instance name. |
target.plugin.input | Optional static input merged into the invocation payload. |
target.agent | Optional agent target using the same shape as workflow agent requests: provider, model, prompt, messages, tools, responseSchema, metadata, providerOptions, and timeout. |
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.plugin.name | Target plugin key from plugins. Required when target.plugin is used. |
target.plugin.operation | Target operation ID. Required when target.plugin is used. |
target.plugin.connection | Optional target connection name. |
target.plugin.instance | Optional target instance name. |
target.plugin.input | Optional static input merged into the invocation payload. |
target.agent | Optional agent target using the same shape as workflow agent requests. |
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 plugin-bound. Configure one or more named entries
under providers.cache, then reference those names from plugins.<name>.cache.
Unlike IndexedDB, caches are not selected through server.providers.
providers:
cache:
session:
source: https://artifacts.example.com/cache/valkey/v0.0.1-alpha.1/provider-release.yaml
config:
address: ${VALKEY_ADDR}
database: 0
plugins:
github:
cache:
- sessionFirst-party cache providers are published at
github.com/valon-technologies/gestalt-providers/cache/<name>.
| Field | Description |
|---|---|
source | Either a local provider manifest path, a published provider-release.yaml metadata URL, or source.githubRelease for a GitHub-hosted release asset. |
auth | Optional inline source auth for remote release sources, including 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 plugins, 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 plugins with
plugins.<name>.s3. Gestalt does not use S3 providers for its own system
state; they are mounted only for plugin 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
plugins:
media:
source: ./plugins/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
plugin can target AWS S3, MinIO, Cloudflare R2, GCS interoperability mode, or
other S3-compatible backends without changing application code.
Plugins 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 plugin/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 a metadata URL source:
providers:
secrets:
gsm:
source: https://artifacts.example.com/secrets/google/v0.0.1-alpha.13/provider-release.yaml
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 plugin 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. |
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 plugin 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.
plugins
Each entry in the plugins map is a named plugin 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 route paths still use “integrations” (e.g. /api/v1/integrations) as stable code surface, while the CLI uses the singular plugin command.
plugins:
github:
displayName: GitHub
description: Public GitHub operations
iconFile: ./icons/github.svg
authorizationPolicy: support_staff
ui:
path: /github
bundle: github_console
source: https://artifacts.example.com/plugin/github/v1.2.3/provider-release.yaml
surfaces:
openapi:
baseUrl: https://api.github.com
indexeddb:
provider: main
db: github_plugin
objectStores:
- tasks
- snapshots
connections:
default:
mode: user
auth:
type: oauth2
allowedOperations:
repos.get:
alias: get_repo
allowedRoles:
- adminPlugin Fields
| Field | Description |
|---|---|
displayName | Human-readable name shown in the UI and API. |
description | Short description of the plugin. |
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 plugin, and the built-in admin UI / admin API can manage plugin-scoped dynamic grants for additional members. Static policy members still take precedence. |
ui | Optional plugin-backed mounted UI binding. Use ui.path plus optional ui.bundle. ui.bundle names a providers.ui entry; omit it to mount the plugin manifest’s owned spec.ui. |
source | Required. The backing provider source. See source shape below. |
source.auth | Optional metadata-fetch auth for release sources. Use this for source.url, source.githubRelease, or local provider-release.yaml metadata. |
auth | Optional plugin route auth reference. Use auth.provider to reference either the reserved server alias or a named entry from providers.authentication. Gestalt enforces this on plugin HTTP operation routes (/api/v1/integrations/{name}/operations and /api/v1/{integration}/{operation}) and on plugin-backed mounted UIs, including browser-login flows whose next path resolves into that plugin’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 plugin 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 plugin 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 declarations. See connections below. |
allowedOperations | Allowlist with optional alias, description, and allowedRoles overrides per operation. |
providerDev.attach.allowedRoles | Roles from this plugin’s authorizationPolicy that may create private remote provider-dev attachments for this plugin. This is denied when omitted, empty, or when the plugin has no authorizationPolicy. |
execution | Optional executable-plugin execution config. execution.mode is local or hosted; execution.runtime carries the hosted runtime fields. |
egress.allowedHosts | Outbound host allowlist. For executable plugins, this is only a complete boundary when a supported sandboxed runtime is active. |
indexeddb | Optional plugin IndexedDB config. indexeddb.provider selects which named providers.indexeddb entry backs the plugin’s single IndexedDB SDK socket; when omitted, the plugin inherits the selected/default host IndexedDB. indexeddb.db overrides the plugin-visible database name and defaults to the plugin key. Schema-capable backends derive plugin scoping from that db name. indexeddb.objectStores optionally allowlists the logical object stores the plugin may use. Plugins 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 plugin through the SDK transport. A single binding also populates the default S3 SDK env var for compatibility. |
S3 bindings are exposed as GESTALT_S3_SOCKET_<NAME>. When exactly one S3
binding is configured, Gestalt also sets GESTALT_S3_SOCKET.
plugins.*.source
Every plugin uses a source block to declare its backing provider.
source: https://artifacts.example.com/plugin/github/v1.2.3/provider-release.yamlCanonical metadata-source auth lives under source.auth. For direct metadata URLs:
source:
url: https://artifacts.example.com/plugin/github/v1.2.3/provider-release.yaml
auth:
token: ${PLUGIN_RELEASE_TOKEN}Plugin route auth overrides are declared separately:
apiVersion: gestaltd.config/v4
plugins:
slack_bot:
source: ./dist/provider-release.yaml
auth:
provider: serverThis field changes runtime authentication for plugin HTTP operation routes and
plugin-backed mounted UIs. Browser login uses the plugin’s route-auth provider
when its next path resolves into that plugin’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.
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: plugins/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
plugins that are registered on the remote server and that they are authorized to
access with gestaltd provider dev --remote. A path-only attach matches the
local manifest source to the remote configured plugin and preserves the
remote plugin config; layered --config files are only needed for explicit
local config overrides or multi-plugin attach sessions.
Remote provider attach is disabled by default on servers. Enable it explicitly on shared remotes:
server:
providerDev:
remoteAttach: true
attachmentState: processLocal
authorization:
policies:
provider_devs:
default: deny
members:
- subjectID: user:usr_123
role: developer
plugins:
slack:
authorizationPolicy: provider_devs
providerDev:
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, plugin config, access tokens, or browser binding
URLs.
Remote attach v1 stores attachments in the gestaltd process that accepted the
create request. attachmentState: processLocal is required as an explicit
operator acknowledgement of that model. For load-balanced deployments, treat
single-replica operation or sticky routing for attach creation, poll/complete
traffic, provider invocations, and provider-owned UI requests as a hard
prerequisite before enabling remoteAttach.
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 plugin runtime grant. For non-interactive/API-token
attach, the caller must satisfy both the plugin runtime grant and a token action
grant. The runtime grant comes from
plugins.<name>.providerDev.attach.allowedRoles on a plugin 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 plugin; normal provider scopes and
operation permissions do not grant attach, and the attach action does not grant
operation invocation.
Gestalt synthesizes Go and Rust executable wrappers automatically when needed and runs Python source providers from the module declared in [tool.gestalt].provider.
Go
A local Go source plugin typically looks like:
my-plugin/
go.mod
manifest.yaml
provider.go| Field | Description |
|---|---|
source | A local provider manifest path, a published provider-release.yaml metadata URL, source.url, or source.githubRelease for a GitHub-hosted release asset. |
source.auth | Optional metadata-fetch auth for release sources, including direct metadata URLs, source.githubRelease, and local provider-release.yaml metadata. |
auth | Optional plugin route auth reference. Use auth.provider to reference either server or a named providers.authentication entry. Gestalt enforces this on plugin HTTP operation routes and on plugin-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 plugins, this is only a complete boundary when a supported sandboxed runtime is active. |
connections
Connections declare how users authenticate to the upstream service. Only connections.default may define params and discovery. A provider manifest can also declare connections; when both the manifest and config define the same connection name, config values are merged on top of the manifest defaults. See Provider Manifests for the manifest-side schema.
connections:
default:
mode: user
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
mcp:
mode: user
auth:
type: mcp_oauthConnection mode determines how credentials are managed: none requires no credentials, and user 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:<plugin>:<binding> as the effective credential owner. All connections in a single plugin must share the same mode.
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. | |
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: user
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 plugins, that declaration is only a complete enforcement
boundary when the deployment is using a supported sandboxed runtime.
server:
egress:
defaultAction: deny
plugins:
github:
egress:
allowedHosts:
- api.github.com
- "*.github.com"
my-plugin:
source: ./plugins/my-plugin/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 plugins, those rules
are only a complete security boundary when the plugin is
running inside a supported sandboxed runtime rather than relying on
host-specific OS wrappers alone.
providers.ui
providers:
ui:
console:
source: https://artifacts.example.com/ui/console/v1.0.0/provider-release.yaml
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 plugins.<name>.ui.bundle binds to plugins.<name>.ui.path. When a plugin manifest declares spec.ui, plugins.<name>.ui.bundle may be omitted entirely and Gestalt synthesizes the mounted UI entry from the plugin-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.
Plugin-backed mounted UIs inherit the owning plugin’s dynamic authorization grants when that plugin 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 plugins.<name>.ui.path. |
authorizationPolicy | Direct policy binding for this UI when not bound through plugins.<name>.ui.path. |
source | A local UI manifest path, a published provider-release.yaml metadata URL, or source.githubRelease for a GitHub-hosted release asset. |
auth | Optional inline source auth for remote release sources, including 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 (init, validate, serve). Runtime validation runs only at serve time.
Structural
Each plugin must set source to exactly one loading mode: a local manifest path or a published provider-release.yaml metadata URL.
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.
Only connections.default may define params or discovery. All connections in a plugin must share the same mode.
Each providers.ui.<name> entry must set source to exactly one loading mode: a local UI manifest path or a published provider-release.yaml metadata URL. providers.ui.<name>.config is only allowed when providers.ui.<name>.source is set.
Each plugins.<name> entry may set ui.path to publish a plugin-backed UI. When ui.bundle is set, it must reference an existing UI bundle. When ui.bundle is omitted, the plugin manifest must declare spec.ui. A given UI may be bound by at most one plugin 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 plugins.<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.