Skip to content

Deployment

fold ships as two artifacts from one codebase. Pick per environment — the config format, auth model, and behavior are identical.

Terminal window
pnpm install && pnpm build
node apps/gateway-node/dist/index.mjs --config fold.config.json --port 8080

Docker:

Terminal window
docker build -f apps/gateway-node/Dockerfile -t fold-gateway .
docker run -p 8080:8080 -v $PWD/fold.config.json:/app/fold.config.json fold-gateway

Multi-instance: set REDIS_URL and every instance shares cache, rate-limit windows, circuit-breaker state, and legacy sessions — any request can land on any instance. Without Redis, stores are in-memory and correct for a single instance only.

Secrets: secretRef values resolve to environment variables (TEST_STATIC_SECRET, FOLD_EMA_KEY, …). Inject via your orchestrator’s secret mechanism.

Health: GET /healthz (liveness), GET /readyz (config loaded), GET /health/upstreams (per-upstream circuit state; Bearer-guarded when auth is required).

Terminal window
cd apps/gateway-workers
npx wrangler deploy
  • FOLD_CONFIG var carries the config JSON (use a secret for configs containing anything sensitive; prefer secretRef + Workers secrets for credentials).
  • FOLD_CACHE KV namespace backs the response/JWKS/token caches (optional — omitting it falls back to in-isolate memory).
  • RATE_LIMITER and SESSIONS Durable Objects provide strongly consistent rate limiting and legacy-session storage; already declared in wrangler.jsonc with SQLite-class migrations.
  • Local development: npx wrangler dev with a .dev.vars file containing FOLD_CONFIG=....

fold instruments itself with OpenTelemetry (@opentelemetry/api): a server span per gateway hop (mcp.method.name, mcp.tool.name, fold.upstream.id, authz decision, status), request/duration metrics, and W3C trace context propagated from callers through to upstreams. Spans materialize when the host process registers a provider:

// Node: alongside the gateway entry, before serving
import { NodeSDK } from '@opentelemetry/sdk-node';
new NodeSDK({ /* OTLP exporter config */ }).start();

Without a registered provider the instrumentation is a no-op — zero configuration required to ignore it.

  • auth.mode: "required" with your IdP as a trusted issuer (or EMA).
  • server.allowedHosts set to your public hostname(s).
  • policy with defaultDecision: "deny" and explicit allow rules.
  • audit.sinks pointed at your log pipeline / SIEM.
  • Upstream auth strategies set (prefer token-exchange); no passthrough without understanding the audience caveat.
  • Node multi-instance: REDIS_URL configured.
  • TLS terminated in front of fold (LB/ingress) — fold serves plain HTTP.