Skip to content

Configuration

fold is configured with a single JSON document, validated on startup (fold --validate). On Node it loads from --config <path> / FOLD_CONFIG (a file path); on Workers from the FOLD_CONFIG var (the JSON itself). The authoritative schema lives in packages/config.

{
"upstreams": [ /* required, at least one */ ],
"auth": { /* optional; default: disabled */ },
"policy": { /* optional; absent = allow-all */ },
"audit": { /* optional; absent = no audit emission */ },
"routing": { /* optional */ },
"server": { /* optional */ }
}

One entry per MCP server folded into the gateway.

FieldDefaultNotes
idLowercase alphanumeric + hyphens. Used in policy, audit, health.
urlThe upstream’s MCP endpoint.
namespacenoneTool/prompt name prefix ({namespace}__{name}). Omitted → zero-copy passthrough; only valid with a single upstream. Required when multiple upstreams are configured.
protocolauto"legacy" routes through the held-session bridge for pre-2026 servers. auto/2026-07-28 proxy over stateless HTTP.
ownernone{ org, team, contact } — which organization runs this upstream. Surfaces in audit and health.
labelsnoneFree-form string map for reporting and future policy selectors.
auth{"strategy":"none"}Upstream credential strategy — see below.
timeouts5s/60s/120sconnectMs, requestMs, streamIdleMs.
circuitBreaker5 / 30sfailureThreshold consecutive failures open the circuit; probes resume after halfOpenAfterMs.
rateLimitnone{ requestsPerMinute } for this upstream only.
StrategyFieldsWhen
noneTrusted network, no upstream auth.
staticsecretRef, header?, scheme?API-key upstreams. secretRef resolves via the runtime’s secret store (env var on Node, Workers secret on the edge).
passthroughForwards the client’s Bearer token as-is. The token’s audience is fold, not the upstream — upstreams doing strict RFC 8707 checks will reject it. Prefer token-exchange.
client-credentialstokenEndpoint, clientId, clientAuth, scopes?, resource?Service identity per upstream (SEP-1046). Tokens cached until 60s before expiry.
token-exchangetokenEndpoint, clientId, clientAuth, audience, scopes?RFC 8693 — exchanges the caller’s token for an upstream-audience token, preserving user identity end-to-end. Recommended enterprise default. Cached per (upstream, subject).

clientAuth: { "type": "client_secret_post" | "client_secret_basic", "secretRef": "..." }.

{
"mode": "required", // "disabled" (default) | "required"
"resource": "https://gw.example.com", // canonical resource URI = required token audience (RFC 8707)
"issuers": [
{
"issuer": "https://acme.okta.com",
"jwksUri": "https://acme.okta.com/oauth2/v1/keys", // default: {issuer}/.well-known/jwks.json
"mode": "direct", // clients present this issuer's tokens directly
"groupsClaim": "groups" // Okta "groups", Entra "roles", Auth0 custom-namespaced
}
],
"ema": { // optional: Enterprise-Managed Authorization
"idpIssuer": "https://acme.okta.com",
"idpJwksUri": "...", // optional
"signingKeyRef": "FOLD_EMA_KEY", // ES256 private key, PKCS8 PEM, via secret store
"tokenTtlSec": 600
}
}

With mode: "required", every /mcp request needs a valid Bearer token: trusted issuer (checked before any network I/O), verified signature via cached JWKS, exact audience match, RS256/ES256/EdDSA only. Failures answer 401 with a WWW-Authenticate challenge pointing at /.well-known/oauth-protected-resource (RFC 9728).

With ema configured, fold exposes POST /oauth/token accepting exactly one grant — the ID-JAG exchange (urn:ietf:params:oauth:grant-type:jwt-bearer) — and publishes its signing key at /.well-known/jwks.json. The enterprise IdP is policy gate one (it decides who gets an ID-JAG); fold’s policy engine is gate two.

{
"defaultDecision": "deny",
"rules": [
{
"id": "eng-github",
"subjects": { "groups": ["engineering"] }, // and/or "subs"; omit → any principal
"clients": ["https://claude.ai/.well-known/cimd"], // CIMD URLs; omit → any client
"allow": [
{ "server": "github", "methods": ["tools/call"], "names": ["get_*", "create_pr"] },
{ "server": "search" } // all methods/names on that upstream
]
}
]
}

First matching rule allows; otherwise defaultDecision. Policy governs named invocations (tools/call, prompts/get) and filters list results per principal — callers never see tools they cannot call. Protocol plumbing (ping, discover, the lists themselves) is not policy-gated; invisibility plus call-denial is the enforcement pair.

{ "sinks": [ { "type": "stdout" }, { "type": "webhook", "url": "https://siem.example.com/ingest", "headers": { "x-api-key": "..." } } ] }

One event per terminal response — including 401s, 403s, and 429s — with principal, upstream, authz decision + rule id, outcome, and latency. See AuditEvent in packages/contracts.

FieldDefaultNotes
mcpPath/mcpPath the gateway serves MCP on.
allowedHostslocalhost setDNS-rebinding protection: allowed Host/Origin hostnames. Set to your public hostname(s) in production, or ["*"] only behind a trusted proxy that controls Host.
rateLimitnoneGlobal { requestsPerMinute } across all upstreams.
FieldDefaultNotes
namespaceSeparator__Fixed in v0.
conflictPolicyprefixReserved for future conflict handling.