From 392ed01c8765c9e41d2a2ad6f09f3b8c3ec7dcd4 Mon Sep 17 00:00:00 2001 From: krish Date: Fri, 7 Aug 2026 11:31:33 +0530 Subject: [PATCH] Update doc version --- en/docs/ai-gateway/1.2.0/README.md | 107 + .../ai-gateway/1.2.0/about-this-release.md | 119 + .../ai-gateway-rest-api/authentication.md | 103 + .../analytics/analytics-header-filter.md | 135 + .../1.2.0/analytics/moesif-analytics.md | 134 + .../deployment-modes/immutable-gateway.md | 171 + .../kubernetes/gateway-operator.md | 515 +++ .../kubernetes/kubernetes-standalone.md | 309 ++ .../deployment-modes/kubernetes/overview.md | 104 + .../guardrails/aws-bedrock-guardrail.md | 291 ++ .../guardrails/azure-content-safety.md | 363 ++ .../llm-proxy/guardrails/content-length.md | 207 + .../1.2.0/llm-proxy/guardrails/json-schema.md | 234 + .../llm-proxy/guardrails/pii-masking-regex.md | 213 + .../1.2.0/llm-proxy/guardrails/regex.md | 217 + .../guardrails/semantic-prompt-guard.md | 410 ++ .../llm-proxy/guardrails/sentence-count.md | 217 + .../1.2.0/llm-proxy/guardrails/url.md | 237 + .../1.2.0/llm-proxy/guardrails/word-count.md | 206 + .../1.2.0/llm-proxy/llm-templates.md | 438 ++ .../load-balancing/model-round-robin.md | 243 + .../model-weighted-round-robin.md | 262 ++ .../1.2.0/llm-proxy/multi-provider-routing.md | 554 +++ .../prompt-management/prompt-decorator.md | 325 ++ .../prompt-management/prompt-template.md | 290 ++ .../1.2.0/llm-proxy/quick-start-guide.md | 229 + .../1.2.0/llm-proxy/semantic-caching.md | 305 ++ .../1.2.0/mcp-proxy/policies/mcp-acl-list.md | 175 + .../mcp-proxy/policies/mcp-authentication.md | 175 + .../mcp-proxy/policies/mcp-authorization.md | 271 ++ .../1.2.0/mcp-proxy/policies/mcp-rewrite.md | 161 + .../1.2.0/mcp-proxy/quick-start-guide.md | 183 + .../ai-gateway/1.2.0/observability/logging.md | 347 ++ .../ai-gateway/1.2.0/observability/tracing.md | 891 ++++ en/docs/ai-gateway/1.2.0/overview.md | 123 + .../ai-gateway-runtime-with-four-cpus.md | 75 + .../ai-gateway-runtime-with-two-cpus.md | 73 + .../ai-gateway/1.2.0/performance/overview.md | 62 + en/docs/ai-gateway/1.2.0/quick-start-guide.md | 230 + .../ai-gateway/1.2.0/resiliency/timeouts.md | 283 ++ .../ai-gateway/1.2.0/setup/configuration.md | 161 + .../ai-gateway/1.2.0/setup/database-setup.md | 335 ++ .../ai-gateway/1.2.0/streaming-responses.md | 103 + .../api-gateway/1.2.0/about-this-release.md | 119 + .../analytics/analytics-header-filter.md | 127 + .../1.2.0/analytics/moesif-analytics.md | 174 + .../bottom-up-api-deployment.md | 680 +++ .../deployment-modes/immutable-gateway.md | 171 + .../gateway-operator-management-crds.md | 726 +++ .../kubernetes/gateway-operator.md | 854 ++++ .../kubernetes/kubernetes-standalone.md | 310 ++ .../deployment-modes/kubernetes/overview.md | 104 + ...high-availability-production-deployment.md | 193 + .../control-plane-connection.md | 163 + .../database-configuration.md | 244 + .../deploy-and-verify.md | 103 + .../resources-and-scaling.md | 144 + .../security-hardening.md | 262 ++ .../authentication.md | 104 + .../certificate-management.md | 265 ++ .../llm-provider-management.md | 1054 +++++ .../llm-provider-template-management.md | 600 +++ .../llm-proxy-management.md | 845 ++++ .../mcp-proxy-management.md | 579 +++ .../overview.md | 134 + .../rest-api-management.md | 1878 ++++++++ .../schemas.md | 3936 +++++++++++++++++ .../secrets-management.md | 438 ++ .../webbroker-api-management.md | 577 +++ .../websub-api-management.md | 893 ++++ .../1.2.0/observability/logging.md | 348 ++ .../metrics/alternative-backends.md | 186 + .../best-practices-and-troubleshooting.md | 326 ++ .../observability/metrics/configuration.md | 73 + .../observability/metrics/enabling-metrics.md | 138 + .../observability/metrics/metric-reference.md | 182 + .../1.2.0/observability/metrics/overview.md | 68 + .../metrics/prometheus-queries.md | 109 + .../metrics/viewing-metrics-in-grafana.md | 155 + .../tracing/alternative-backends.md | 326 ++ .../best-practices-and-troubleshooting.md | 183 + .../observability/tracing/configuration.md | 211 + .../observability/tracing/enabling-tracing.md | 94 + .../1.2.0/observability/tracing/overview.md | 64 + .../tracing/viewing-traces-in-jaeger.md | 98 + .../1.2.0/observability/traffic-logging.md | 264 ++ en/docs/api-gateway/1.2.0/overview.md | 89 + .../gateway-runtime-with-four-cpus.md | 65 + .../gateway-runtime-with-two-cpus.md | 76 + .../api-gateway/1.2.0/performance/overview.md | 61 + .../building-gateway-with-custom-policies.md | 291 ++ .../writing-a-custom-policy.md | 368 ++ .../api-gateway/1.2.0/policies/overview.md | 266 ++ .../api-gateway/1.2.0/quick-start-guide.md | 167 + .../api-gateway/1.2.0/resiliency/timeouts.md | 269 ++ .../1.2.0/setup/artifact-templating.md | 119 + .../api-gateway/1.2.0/setup/configuration.md | 162 + .../api-gateway/1.2.0/setup/database-setup.md | 337 ++ 98 files changed, 31358 insertions(+) create mode 100644 en/docs/ai-gateway/1.2.0/README.md create mode 100644 en/docs/ai-gateway/1.2.0/about-this-release.md create mode 100644 en/docs/ai-gateway/1.2.0/ai-gateway-rest-api/authentication.md create mode 100644 en/docs/ai-gateway/1.2.0/analytics/analytics-header-filter.md create mode 100644 en/docs/ai-gateway/1.2.0/analytics/moesif-analytics.md create mode 100644 en/docs/ai-gateway/1.2.0/deployment-modes/immutable-gateway.md create mode 100644 en/docs/ai-gateway/1.2.0/deployment-modes/kubernetes/gateway-operator.md create mode 100644 en/docs/ai-gateway/1.2.0/deployment-modes/kubernetes/kubernetes-standalone.md create mode 100644 en/docs/ai-gateway/1.2.0/deployment-modes/kubernetes/overview.md create mode 100644 en/docs/ai-gateway/1.2.0/llm-proxy/guardrails/aws-bedrock-guardrail.md create mode 100644 en/docs/ai-gateway/1.2.0/llm-proxy/guardrails/azure-content-safety.md create mode 100644 en/docs/ai-gateway/1.2.0/llm-proxy/guardrails/content-length.md create mode 100644 en/docs/ai-gateway/1.2.0/llm-proxy/guardrails/json-schema.md create mode 100644 en/docs/ai-gateway/1.2.0/llm-proxy/guardrails/pii-masking-regex.md create mode 100644 en/docs/ai-gateway/1.2.0/llm-proxy/guardrails/regex.md create mode 100644 en/docs/ai-gateway/1.2.0/llm-proxy/guardrails/semantic-prompt-guard.md create mode 100644 en/docs/ai-gateway/1.2.0/llm-proxy/guardrails/sentence-count.md create mode 100644 en/docs/ai-gateway/1.2.0/llm-proxy/guardrails/url.md create mode 100644 en/docs/ai-gateway/1.2.0/llm-proxy/guardrails/word-count.md create mode 100644 en/docs/ai-gateway/1.2.0/llm-proxy/llm-templates.md create mode 100644 en/docs/ai-gateway/1.2.0/llm-proxy/load-balancing/model-round-robin.md create mode 100644 en/docs/ai-gateway/1.2.0/llm-proxy/load-balancing/model-weighted-round-robin.md create mode 100644 en/docs/ai-gateway/1.2.0/llm-proxy/multi-provider-routing.md create mode 100644 en/docs/ai-gateway/1.2.0/llm-proxy/prompt-management/prompt-decorator.md create mode 100644 en/docs/ai-gateway/1.2.0/llm-proxy/prompt-management/prompt-template.md create mode 100644 en/docs/ai-gateway/1.2.0/llm-proxy/quick-start-guide.md create mode 100644 en/docs/ai-gateway/1.2.0/llm-proxy/semantic-caching.md create mode 100644 en/docs/ai-gateway/1.2.0/mcp-proxy/policies/mcp-acl-list.md create mode 100644 en/docs/ai-gateway/1.2.0/mcp-proxy/policies/mcp-authentication.md create mode 100644 en/docs/ai-gateway/1.2.0/mcp-proxy/policies/mcp-authorization.md create mode 100644 en/docs/ai-gateway/1.2.0/mcp-proxy/policies/mcp-rewrite.md create mode 100644 en/docs/ai-gateway/1.2.0/mcp-proxy/quick-start-guide.md create mode 100644 en/docs/ai-gateway/1.2.0/observability/logging.md create mode 100644 en/docs/ai-gateway/1.2.0/observability/tracing.md create mode 100644 en/docs/ai-gateway/1.2.0/overview.md create mode 100644 en/docs/ai-gateway/1.2.0/performance/ai-gateway-runtime-with-four-cpus.md create mode 100644 en/docs/ai-gateway/1.2.0/performance/ai-gateway-runtime-with-two-cpus.md create mode 100644 en/docs/ai-gateway/1.2.0/performance/overview.md create mode 100644 en/docs/ai-gateway/1.2.0/quick-start-guide.md create mode 100644 en/docs/ai-gateway/1.2.0/resiliency/timeouts.md create mode 100644 en/docs/ai-gateway/1.2.0/setup/configuration.md create mode 100644 en/docs/ai-gateway/1.2.0/setup/database-setup.md create mode 100644 en/docs/ai-gateway/1.2.0/streaming-responses.md create mode 100644 en/docs/api-gateway/1.2.0/about-this-release.md create mode 100644 en/docs/api-gateway/1.2.0/analytics/analytics-header-filter.md create mode 100644 en/docs/api-gateway/1.2.0/analytics/moesif-analytics.md create mode 100644 en/docs/api-gateway/1.2.0/deployment/deploying-apis/bottom-up-api-deployment.md create mode 100644 en/docs/api-gateway/1.2.0/deployment/deployment-modes/immutable-gateway.md create mode 100644 en/docs/api-gateway/1.2.0/deployment/deployment-modes/kubernetes/gateway-operator-management-crds.md create mode 100644 en/docs/api-gateway/1.2.0/deployment/deployment-modes/kubernetes/gateway-operator.md create mode 100644 en/docs/api-gateway/1.2.0/deployment/deployment-modes/kubernetes/kubernetes-standalone.md create mode 100644 en/docs/api-gateway/1.2.0/deployment/deployment-modes/kubernetes/overview.md create mode 100644 en/docs/api-gateway/1.2.0/deployment/high-availability-production-deployment.md create mode 100644 en/docs/api-gateway/1.2.0/deployment/production-deployment/control-plane-connection.md create mode 100644 en/docs/api-gateway/1.2.0/deployment/production-deployment/database-configuration.md create mode 100644 en/docs/api-gateway/1.2.0/deployment/production-deployment/deploy-and-verify.md create mode 100644 en/docs/api-gateway/1.2.0/deployment/production-deployment/resources-and-scaling.md create mode 100644 en/docs/api-gateway/1.2.0/deployment/production-deployment/security-hardening.md create mode 100644 en/docs/api-gateway/1.2.0/gateway-controller-management-api/authentication.md create mode 100644 en/docs/api-gateway/1.2.0/gateway-controller-management-api/certificate-management.md create mode 100644 en/docs/api-gateway/1.2.0/gateway-controller-management-api/llm-provider-management.md create mode 100644 en/docs/api-gateway/1.2.0/gateway-controller-management-api/llm-provider-template-management.md create mode 100644 en/docs/api-gateway/1.2.0/gateway-controller-management-api/llm-proxy-management.md create mode 100644 en/docs/api-gateway/1.2.0/gateway-controller-management-api/mcp-proxy-management.md create mode 100644 en/docs/api-gateway/1.2.0/gateway-controller-management-api/overview.md create mode 100644 en/docs/api-gateway/1.2.0/gateway-controller-management-api/rest-api-management.md create mode 100644 en/docs/api-gateway/1.2.0/gateway-controller-management-api/schemas.md create mode 100644 en/docs/api-gateway/1.2.0/gateway-controller-management-api/secrets-management.md create mode 100644 en/docs/api-gateway/1.2.0/gateway-controller-management-api/webbroker-api-management.md create mode 100644 en/docs/api-gateway/1.2.0/gateway-controller-management-api/websub-api-management.md create mode 100644 en/docs/api-gateway/1.2.0/observability/logging.md create mode 100644 en/docs/api-gateway/1.2.0/observability/metrics/alternative-backends.md create mode 100644 en/docs/api-gateway/1.2.0/observability/metrics/best-practices-and-troubleshooting.md create mode 100644 en/docs/api-gateway/1.2.0/observability/metrics/configuration.md create mode 100644 en/docs/api-gateway/1.2.0/observability/metrics/enabling-metrics.md create mode 100644 en/docs/api-gateway/1.2.0/observability/metrics/metric-reference.md create mode 100644 en/docs/api-gateway/1.2.0/observability/metrics/overview.md create mode 100644 en/docs/api-gateway/1.2.0/observability/metrics/prometheus-queries.md create mode 100644 en/docs/api-gateway/1.2.0/observability/metrics/viewing-metrics-in-grafana.md create mode 100644 en/docs/api-gateway/1.2.0/observability/tracing/alternative-backends.md create mode 100644 en/docs/api-gateway/1.2.0/observability/tracing/best-practices-and-troubleshooting.md create mode 100644 en/docs/api-gateway/1.2.0/observability/tracing/configuration.md create mode 100644 en/docs/api-gateway/1.2.0/observability/tracing/enabling-tracing.md create mode 100644 en/docs/api-gateway/1.2.0/observability/tracing/overview.md create mode 100644 en/docs/api-gateway/1.2.0/observability/tracing/viewing-traces-in-jaeger.md create mode 100644 en/docs/api-gateway/1.2.0/observability/traffic-logging.md create mode 100644 en/docs/api-gateway/1.2.0/overview.md create mode 100644 en/docs/api-gateway/1.2.0/performance/gateway-runtime-with-four-cpus.md create mode 100644 en/docs/api-gateway/1.2.0/performance/gateway-runtime-with-two-cpus.md create mode 100644 en/docs/api-gateway/1.2.0/performance/overview.md create mode 100644 en/docs/api-gateway/1.2.0/policies/custom-policies/building-gateway-with-custom-policies.md create mode 100644 en/docs/api-gateway/1.2.0/policies/custom-policies/writing-a-custom-policy.md create mode 100644 en/docs/api-gateway/1.2.0/policies/overview.md create mode 100644 en/docs/api-gateway/1.2.0/quick-start-guide.md create mode 100644 en/docs/api-gateway/1.2.0/resiliency/timeouts.md create mode 100644 en/docs/api-gateway/1.2.0/setup/artifact-templating.md create mode 100644 en/docs/api-gateway/1.2.0/setup/configuration.md create mode 100644 en/docs/api-gateway/1.2.0/setup/database-setup.md diff --git a/en/docs/ai-gateway/1.2.0/README.md b/en/docs/ai-gateway/1.2.0/README.md new file mode 100644 index 000000000..0c57b2885 --- /dev/null +++ b/en/docs/ai-gateway/1.2.0/README.md @@ -0,0 +1,107 @@ +--- +title: "API Platform AI Gateway" +description: "Overview of API Platform AI Gateway for managing LLM and MCP traffic, covering key concepts, architecture, and quick start links." +canonical_url: https://wso2.com/api-platform/docs/ai-gateway/ +md_url: https://wso2.com/api-platform/docs/ai-gateway.md +tags: + - ai-gateway + - llm + - mcp +author: WSO2 API Platform Documentation Team +last_updated: 2026-06-16 +content_type: "overview" +--- + +# API Platform AI Gateway + +A gateway for managing and securing AI traffic, including Large Language Model (LLM) APIs and Model Context Protocol (MCP) servers. + +## Quick Start + +- [LLM Quick Start Guide](llm-proxy/quick-start-guide.md) - Set up the gateway and route traffic to LLM providers like OpenAI +- [MCP Quick Start Guide](mcp-proxy/quick-start-guide.md) - Set up the gateway and route traffic to MCP servers + +## Key Concepts + +### LLM Provider Template + +An LLM Provider Template defines the characteristics and behaviors specific to an AI service provider, such as OpenAI, Azure OpenAI, or other LLM platforms. It describes how the gateway should interpret and extract usage and operational metadata, including prompt, completion, total, and remaining token information, as well as request and response model metadata. + +Following templates are shipped out-of-the-box + +- OpenAI +- Azure OpenAI +- Anthropic +- AWS Bedrock +- Azure AI Foundry +- Gemini + +### LLM Provider + +An LLM Provider represents a connection to an AI backend service such as OpenAI, Azure OpenAI, or other LLM APIs. Platform administrators configure LLM Providers to define: + +- The LLM Provider Template +- The upstream LLM service URL +- Authentication credentials (API keys, tokens) +- Access control rules for which endpoints are exposed +- Budget control policies, such as token-based rate limiting +- Organization-wide policies such as guardrails + +Once configured, the LLM Provider allows traffic to flow through the gateway to the AI backend. + +### LLM Proxy + +An LLM Proxy allows developers to create custom API endpoints that consume an LLM Provider, while inheriting administrator-enforced access control, budgeting and organization-wide policies defined at the provider level. Each proxy gets its own URL context (e.g., `/assistant`) and can have its own policies applied. This enables: + +- Multiple AI applications to share a single LLM Provider +- Per-application policies such as prompt management and guardrails +- Separation between platform administration and application development + +### MCP Proxy + +An MCP Proxy routes Model Context Protocol traffic to MCP servers. MCP is a protocol that enables AI assistants to interact with external tools and data sources. With MCP Proxies, you can: + +- Expose MCP servers through a centralized gateway +- Apply authentication and access control to MCP traffic +- Manage multiple MCP servers from a single control plane + +## Default Ports + +| Port | Service | Description | +|------|---------|-------------| +| 8080 | Router | HTTP traffic | +| 8443 | Router | HTTPS traffic | +| 9090 | Gateway-Controller | REST API | + +## Architecture + +``` + ┌─────────────────┐ + │ LLM Providers │ + │ (OpenAI, etc.) │ + └────────▲────────┘ + │ +┌──────────┐ ┌──────────────┐ │ +│ AI Apps │───▶│ AI Gateway │────┤ +└──────────┘ └──────────────┘ │ + │ + ┌────────▼────────┐ + │ MCP Servers │ + └─────────────────┘ +``` + +**How it works:** + +1. Administrators configure LLM Providers and MCP Proxies via the Gateway-Controller API +2. Developers create LLM Proxies to build AI applications on top of available providers +3. The gateway routes traffic, applies policies, and manages authentication + +## Documentation + +| Section | Description | +|---------|-------------| +| [llm/](llm/) | LLM provider configuration, guardrails, prompt management, and semantic caching | +| [mcp/](mcp/) | MCP proxy setup and policies | +| [observability/](observability/) | Logging and tracing configuration | +| [analytics/](analytics/) | Analytics integrations (Moesif) | +| [ai-gateway-rest-api/](ai-gateway-rest-api/) | REST API authentication and usage | diff --git a/en/docs/ai-gateway/1.2.0/about-this-release.md b/en/docs/ai-gateway/1.2.0/about-this-release.md new file mode 100644 index 000000000..836b8b221 --- /dev/null +++ b/en/docs/ai-gateway/1.2.0/about-this-release.md @@ -0,0 +1,119 @@ +# AI Gateway Changelog + +**Release date:** 2026-07-07 +**Previous version:** 1.1.0 (2026-04-30) + +### New feature additions + +- **Traffic logging support:** Added traffic logging capabilities for gateway request and response flows. +- **Timeout functionality support:** Added timeout configuration support for gateway traffic handling. +- **MSSQL support for gateway:** Added Microsoft SQL Server support for gateway persistence. +- **Multiple virtual host support:** The `endpoints` array replaces the single `vhost` field, allowing one gateway to expose multiple virtual hosts simultaneously ([#2128](https://github.com/wso2/api-platform/issues/2128)). +- **`upstreamDefinitions` URL improvements:** Upstream definition URLs support query strings and path fragments correctly ([#2068](https://github.com/wso2/api-platform/issues/2068)). +- **Kubernetes Gateway API conformance support:** Added Kubernetes Gateway API conformance support. +- **Multi-provider routing:** Added routing support across multiple providers. + +#### New policies + +- **[Opaque token authentication](https://wso2.com/api-platform/policy-hub/policies/opaque-token-auth)** +- **[Backend JWT](https://wso2.com/api-platform/policy-hub/policies/backend-jwt)** +- **[AWS authentication](https://wso2.com/api-platform/policy-hub/policies/aws-authentication):** +- **[MCP ratelimit](https://wso2.com/api-platform/policy-hub/policies/mcp-ratelimit)** +- **[Granite Guardian Prompt Injection](https://wso2.com/api-platform/policy-hub/policies/granite-guardian-prompt-injection)** +- **[OpenAI to Anthropic Transformer](https://wso2.com/api-platform/policy-hub/policies/openai-to-anthropic-transformer)** +- **[OpenAI to Azure OpenAI Transformer](https://wso2.com/api-platform/policy-hub/policies/openai-to-azure-openai-transformer)** +- **[OpenAI to Bedrock Transformer](https://wso2.com/api-platform/policy-hub/policies/openai-to-bedrock-transformer)** +- **[OpenAI to Gemini Transformer](https://wso2.com/api-platform/policy-hub/policies/openai-to-gemini-transformer)** +- **[OpenAI to Mistral Transformer](https://wso2.com/api-platform/policy-hub/policies/openai-to-mistral-transformer)** + +See [Policy Hub](https://wso2.com/api-platform/policy-hub) for policy references. + +### Added + +- `feat(llm)`: Multi-provider routing for LLM proxies ([9288a1e20](https://github.com/wso2/api-platform/commit/9288a1e20)). +- `feat(gateway)`: Multi-provider model round-robin routing ([2878f7b90](https://github.com/wso2/api-platform/commit/2878f7b90)). +- Added AWS Bedrock multi-provider support ([6da796b0e](https://github.com/wso2/api-platform/commit/6da796b0e)). +- Added opt-in `pprof` endpoints on admin servers ([da3d42c0b](https://github.com/wso2/api-platform/commit/da3d42c0b)). +- Added global traffic logging support and field-exclusion controls ([d484dd7bb](https://github.com/wso2/api-platform/commit/d484dd7bb), [bd2f451f7](https://github.com/wso2/api-platform/commit/bd2f451f7), [58eb41727](https://github.com/wso2/api-platform/commit/58eb41727)). +- Added support for request/response body and header capture options in traffic logging ([8a3b33f4f](https://github.com/wso2/api-platform/commit/8a3b33f4f)). +- Added policy-engine context snapshots for downstream path/method and upstream status ([61a68890e](https://github.com/wso2/api-platform/commit/61a68890e)). +- Added policy metadata support for analytics/traffic logging ([6ad01d3b3](https://github.com/wso2/api-platform/commit/6ad01d3b3)). +- Added SQL Server support and related schema/distribution updates ([505abca5f](https://github.com/wso2/api-platform/commit/505abca5f), [4707e2b38](https://github.com/wso2/api-platform/commit/4707e2b38), [9eff6defd](https://github.com/wso2/api-platform/commit/9eff6defd), [c70f3ff1c](https://github.com/wso2/api-platform/commit/c70f3ff1c)). +- Added gateway health check implementation ([4fea1ce05](https://github.com/wso2/api-platform/commit/4fea1ce05)). +- Added basic auth protection for admin service (health endpoint excluded) ([ac2129858](https://github.com/wso2/api-platform/commit/ac2129858)). +- Added role-based admin validation and enforced role-claims when IDP is enabled ([d53b8844d](https://github.com/wso2/api-platform/commit/d53b8844d), [bf8342c62](https://github.com/wso2/api-platform/commit/bf8342c62)). +- Added support for multiple `-config` files in both controller and policy-engine ([c6b343a87](https://github.com/wso2/api-platform/commit/c6b343a87), [073cb77bf](https://github.com/wso2/api-platform/commit/073cb77bf)). +- Added `set-headers` append mode support (policy update from [#2103](https://github.com/wso2/api-platform/issues/2103)). +- Added HTTP connection manager (downstream) timeout config support in runtime config model. + +### Changed + +- Refactored policy definition sourcing to use gateway-builder output ([0f8b59aeb](https://github.com/wso2/api-platform/commit/0f8b59aeb)). +- Updated routing model: moved header-based routing and redirect logic out of API YAML into dedicated policies ([22198fd11](https://github.com/wso2/api-platform/commit/22198fd11), [eec5e2fa7](https://github.com/wso2/api-platform/commit/eec5e2fa7), [504d33bdf](https://github.com/wso2/api-platform/commit/504d33bdf), [d0f84a780](https://github.com/wso2/api-platform/commit/d0f84a780)). +- Updated operation routing model to use a new `match` object for path match type and header-based matching (from [#2103](https://github.com/wso2/api-platform/issues/2103)). +- Enabled path normalization by default ([c7979cf16](https://github.com/wso2/api-platform/commit/c7979cf16)). +- Updated policy bundles/versions, including API key and auth policy lines ([f67181bb5](https://github.com/wso2/api-platform/commit/f67181bb5), [ff1965e9b](https://github.com/wso2/api-platform/commit/ff1965e9b), [cb788c033](https://github.com/wso2/api-platform/commit/cb788c033), [855da688b](https://github.com/wso2/api-platform/commit/855da688b), [d573b07a8](https://github.com/wso2/api-platform/commit/d573b07a8), [c7553aa1a](https://github.com/wso2/api-platform/commit/c7553aa1a)). +- Refined config interpolation and overrides, including env/file interpolation and allowlist behavior ([bff0bc3ee](https://github.com/wso2/api-platform/commit/bff0bc3ee), [47e74040d](https://github.com/wso2/api-platform/commit/47e74040d), [eca558827](https://github.com/wso2/api-platform/commit/eca558827), [4a9a263a7](https://github.com/wso2/api-platform/commit/4a9a263a7)). +- Updated runtime/build baselines (Go toolchain and Envoy updates) ([f4ff5034c](https://github.com/wso2/api-platform/commit/f4ff5034c), [a38b95f5f](https://github.com/wso2/api-platform/commit/a38b95f5f), [be297cf32](https://github.com/wso2/api-platform/commit/be297cf32)). +- Included distribution docs and packaging updates for release artifacts ([41f89cde7](https://github.com/wso2/api-platform/commit/41f89cde7), [c70f3ff1c](https://github.com/wso2/api-platform/commit/c70f3ff1c)). +- Bumped gateway controller REST API base paths to `v1` and aligned artifact API versioning to `gateway.api-platform.wso2.com/v1`. +- Added `data_version` handling for gateway-controller artifacts to decouple stored data shape from wire version ([290606d8a](https://github.com/wso2/api-platform/commit/290606d8a), [70a86c6e3](https://github.com/wso2/api-platform/commit/70a86c6e3)). +- Changed custom policy `managedBy` default/normalized value from `customer` to `organization`. +- Updated analytics/collector config model: renamed gRPC collector section and split payload controls for request/response bodies and headers. +- Moved toward config-driven env/file interpolation for runtime config management via `config.toml` templates, plus [bff0bc3ee](https://github.com/wso2/api-platform/commit/bff0bc3ee) and [47e74040d](https://github.com/wso2/api-platform/commit/47e74040d). + +### Config Changes + +- Added new `traffic_logging` config block: + +```toml +[traffic_logging] +enabled = false +masked_headers = ["authorization", "x-api-key", "x-jwt-assertion"] +max_payload_size = 0 +request_headers = false +request_body = false +response_headers = false +response_body = false + +[traffic_logging.properties] +``` + +- Collector and analytics config changes: + - Renamed `[analytics.gprc_event_server]` to `[collector.server]`. + - Removed `[analytics.gprc_event_server].server_port`. + - Replaced analytics payload toggles with collector-level request/response controls: + +```toml +[collector] +request_body = false +response_body = false +request_headers = false +response_headers = false +ignored_path_prefixes = [] +``` + +- Added downstream HTTP listener timeout block: + +```toml +[router.http_listener.timeouts] +request_timeout = "0s" +request_headers_timeout = "0s" +stream_idle_timeout = "5m" +idle_timeout = "1h" +``` + +- Configuration source model update: direct `APIP_*` runtime overrides are removed in favor of `{{ env }}` / `{{ file }}` interpolation in `config.toml`, with required env vars supplied via env files. + +### Fixed + +- Fixed stale extracted policy artifacts in gateway runtime ([6b07c59c4](https://github.com/wso2/api-platform/commit/6b07c59c4)). +- Fixed controller behavior for overlapping/same-name LLM policy matches ([746db0ed9](https://github.com/wso2/api-platform/commit/746db0ed9), [14451b47e](https://github.com/wso2/api-platform/commit/14451b47e)). +- Fixed dynamic endpoint/base path handling in gateway and controller ([7cff05d2f](https://github.com/wso2/api-platform/commit/7cff05d2f), [30a24d2b7](https://github.com/wso2/api-platform/commit/30a24d2b7), [ad921c208](https://github.com/wso2/api-platform/commit/ad921c208)). +- Fixed `upstreamDefinitions` URL validation and error clarity (query/fragment rejection) ([227e19bc5](https://github.com/wso2/api-platform/commit/227e19bc5), [d5bc0b535](https://github.com/wso2/api-platform/commit/d5bc0b535), [d267f3346](https://github.com/wso2/api-platform/commit/d267f3346), [a43762260](https://github.com/wso2/api-platform/commit/a43762260)). +- Fixed xDS snapshot update race with synchronization improvements ([62b7f22a8](https://github.com/wso2/api-platform/commit/62b7f22a8), [b87094a43](https://github.com/wso2/api-platform/commit/b87094a43)). +- Fixed graceful runtime shutdown by draining Router ([271cc4c2b](https://github.com/wso2/api-platform/commit/271cc4c2b)). +- Fixed duplicate analytics event emission in LLM proxy flows and hardened loopback suppression ([995a97edc](https://github.com/wso2/api-platform/commit/995a97edc), [07c5fe644](https://github.com/wso2/api-platform/commit/07c5fe644), [9b4f54d23](https://github.com/wso2/api-platform/commit/9b4f54d23), [447d5a7bb](https://github.com/wso2/api-platform/commit/447d5a7bb)). +- Fixed policy-engine update behavior to retain unchanged chains and avoid false route removals ([edb2e453d](https://github.com/wso2/api-platform/commit/edb2e453d), [066695c7a](https://github.com/wso2/api-platform/commit/066695c7a)). +- Fixed LLM provider validation/sync edge cases, including duplicate-named API-level policy preservation and invalid config rejection ([6ce9cbde3](https://github.com/wso2/api-platform/commit/6ce9cbde3), [2843f9eb5](https://github.com/wso2/api-platform/commit/2843f9eb5)). +- Fixed translator/runtime handling for timeout mapping and upstream selection continuity ([34a82c1bc](https://github.com/wso2/api-platform/commit/34a82c1bc), [6e79649c7](https://github.com/wso2/api-platform/commit/6e79649c7), [0a98f265b](https://github.com/wso2/api-platform/commit/0a98f265b)). diff --git a/en/docs/ai-gateway/1.2.0/ai-gateway-rest-api/authentication.md b/en/docs/ai-gateway/1.2.0/ai-gateway-rest-api/authentication.md new file mode 100644 index 000000000..63e56f1ac --- /dev/null +++ b/en/docs/ai-gateway/1.2.0/ai-gateway-rest-api/authentication.md @@ -0,0 +1,103 @@ +--- +title: "Authentication and Authorization in Gateway Controller" +description: "Configure Basic Auth or JWT/IDP authentication and role-based authorization for the AI Gateway Controller REST API." +canonical_url: https://wso2.com/api-platform/docs/ai-gateway/ai-gateway-rest-api/authentication/ +md_url: https://wso2.com/api-platform/docs/ai-gateway/ai-gateway-rest-api/authentication.md +tags: + - ai-gateway + - security + - authentication +author: WSO2 API Platform Documentation Team +last_updated: 2026-06-16 +content_type: "how-to" +--- + +# Authentication & Authorization in Gateway Controller + +## Overview +The Gateway Controller REST API (the control-plane API used to manage gateway configuration) can be protected using either locally configured users (Basic Auth) or an external Identity Provider (JWT validation via JWKS). Authorization is role-based and enforced per API route. + +## How It Works + +### Authentication (Who are you?) +You can enable one (or both) of the following: + +- **Basic Auth (local users)**: Define usernames/passwords and assign local roles. +- **IDP/JWT (external users)**: Validate incoming JWTs using `jwks_url` optionally `issuer`. + +**No Authentication (open access)**: If BOTH `basic.enabled` and `idp.enabled` are set to `false`, all requests to the gateway controller are allowed without authentication. + +### Authorization (Are you allowed?) +Gateway Controller routes are protected using **local roles** (for example `admin`, `developer`, `consumer`). + +- If **`roles_claim` is NOT configured** in the IDP/JWT setup, **authorization is bypassed** for the Gateway Controller REST API routes (i.e., no role checks are performed). +- If **`roles_claim` IS configured**, you **must** also configure **`role_mapping`**. Without a mapping, the controller cannot translate IDP roles → local roles, and requests will be denied. + +## Configuration +In the umbrella gateway config, these settings live under `gateway_controller.auth`. (If you run the controller standalone, the same structure applies under the controller’s config root.) + +### Option A: Basic Auth (local users) +```yaml +controller: + auth: + basic: + enabled: true + users: + - username: "platform-admin" + password: "$bcrypt$..." + password_hashed: true + roles: ["admin"] + - username: "ops" + password: "ops" + password_hashed: false + roles: ["developer"] +``` + +### Option B: JWT (IDP) with role-based authorization +```yaml +controller: + auth: + idp: + enabled: true + jwks_url: "https://idp.example.com/oauth2/jwks" + issuer: "https://idp.example.com/oauth2/token" + + # Turn ON authorization by providing BOTH: + roles_claim: "groups" # e.g., "groups", "scope", "roles" + role_mapping: + admin: ["gateway-admins", "platform-admins"] + developer: ["api-developers", "ops"] + # Optional: give any authenticated user a baseline role + consumer: ["*"] +``` + +## Role Mapping Semantics +`role_mapping` is defined as: + +```text +local_role -> [idp_role_value_1, idp_role_value_2, ...] +``` + +Notes: +- **Specific mappings take precedence** over wildcard matches. +- **Wildcard (`"*"`)** means: if a JWT role value does not match any specific mapping, it can still map to the local role that includes `"*"`. +- **One JWT role can grant multiple local roles** by listing it under multiple local roles. +- **Wildcard mapping must be unique**: Do not configure more than one local role with `"*"` (for example `admin: ["*"]` and `consumer: ["*"]`). The Gateway Controller validates configuration and rejects multiple wildcard roles in `role_mapping`. + +### Example: One IDP group grants multiple local roles +```yaml +role_mapping: + admin: ["platform-admins"] + developer: ["platform-admins", "api-developers"] +``` +In this example, a user in `platform-admins` becomes both `admin` and `developer` in the Gateway Controller. + +## Troubleshooting (What you’ll observe) +- **Requests are denied after enabling JWT auth**: verify `jwks_url` and (if set) `issuer` match the token you're sending. +- **You enabled `roles_claim` and suddenly everything is forbidden**: add `role_mapping` (mapping is mandatory when `roles_claim` is provided). +- **Users authenticate but don't have expected access**: confirm the token actually contains the configured `roles_claim`, and that its values match what you listed in `role_mapping`. +- **You want authN but not authZ**: keep IDP enabled, but leave `roles_claim` and `role_mapping` unset to bypass authorization checks. +- **You want to disable auth entirely**: set both `basic.enabled` and `idp.enabled` to `false`. + +## Testing +Unit tests cover wildcard precedence, one-to-many role grants, and supported claim formats for `roles_claim`. diff --git a/en/docs/ai-gateway/1.2.0/analytics/analytics-header-filter.md b/en/docs/ai-gateway/1.2.0/analytics/analytics-header-filter.md new file mode 100644 index 000000000..f312a6397 --- /dev/null +++ b/en/docs/ai-gateway/1.2.0/analytics/analytics-header-filter.md @@ -0,0 +1,135 @@ +--- +title: "Analytics Header Filter" +description: "Control which request and response headers are sent to analytics backends using allow or deny mode in API Platform AI Gateway." +canonical_url: https://wso2.com/api-platform/docs/ai-gateway/analytics/analytics-header-filter/ +md_url: https://wso2.com/api-platform/docs/ai-gateway/analytics/analytics-header-filter.md +tags: + - ai-gateway + - analytics + - policies +author: WSO2 API Platform Documentation Team +last_updated: 2026-06-16 +content_type: "reference" +--- + +# Analytics Header Filter + +## Overview + +The Analytics Header Filter policy allows you to control which request and response headers are included in analytics data using allow or deny modes. This policy is intended to prevent sensitive, noisy, or irrelevant headers from being sent to analytics backends while preserving the rest of the request and response context. + +The policy is only effective when analytics is enabled at the system level and must be explicitly added to the API’s policy chain. + +**Operation modes:** +- **"allow"**: Only the specified headers will be included in analytics (whitelist mode) +- **"deny"**: All headers except the specified ones will be included in analytics (blacklist mode) + +Request and response headers can have different operation modes, allowing for flexible filtering strategies. + + +## Features + +* Filters request and response headers from analytics data collection using allow or deny modes +* Case-insensitive header matching +* Supports independent configuration with flexible filtering strategies with whitelist (allow) and blacklist (deny) modes +* Operates transparently without affecting request or response processing +* Helps protect sensitive information from being exposed in analytics systems + + +## Configuration + +### Parameters + +| Parameter | Type | Required | Default | Description | +| ------------------------- | ------ | -------- | ------- | ---------------------------------------------------------------------------------------------------------- | +| `requestHeadersToFilter` | object | No | - | Configuration for filtering request headers. Contains `operation` and `headers` properties. | +| `responseHeadersToFilter` | object | No | - | Configuration for filtering response headers. Contains `operation` and `headers` properties. | + +### Parameter Structure + +Each filter parameter (`requestHeadersToFilter` and `responseHeadersToFilter`) is an object with the following properties: + +| Property | Type | Required | Description | +| ----------- | ------ | -------- | ---------------------------------------------------------------------------------------------------------- | +| `operation` | string | Yes | Operation mode: `"allow"` (whitelist) or `"deny"` (blacklist). Header names are matched case-insensitively. | +| `headers` | array | Yes | List of header names to filter. Behavior depends on the operation mode. Each header name must be 1-256 characters. | + +> **Note**: This policy only affects analytics data collection. It does not remove or modify headers sent to upstream services or returned to clients. + + +## System Requirements + +* Analytics must be enabled globally via `config.yaml` (`analytics.enabled: true`) +* The policy must be explicitly applied to the API policy chain +* If analytics is disabled at the system level, this policy has no effect + + +## API Definition Example + +The following example demonstrates how to apply the Analytics Header Filter policy to a LlmProvider: + +```bash +curl -X POST http://localhost:9090/llm-providers \ + -H "Content-Type: application/yaml" \ + -H "Authorization: Basic " \ + --data-binary @- <<'EOF' +apiVersion: gateway.api-platform.wso2.com/v1alpha1 +kind: LlmProvider +metadata: + name: openai-provider +spec: + displayName: OpenAI Provider + version: v1.0 + template: openai + upstream: + url: https://api.openai.com/v1 + auth: + type: api-key + header: Authorization + value: Bearer + accessControl: + mode: deny_all + exceptions: + - path: /chat/completions + methods: [POST] + policies: + - name: analytics-header-filter + version: v0.1.0 + params: + requestHeadersToFilter: + operation: deny + headers: + - "authorization" + - "x-api-key" + responseHeadersToFilter: + operation: allow + headers: + - "content-type" + - path: /models + methods: [GET] + - path: /models/{modelId} + methods: [GET] +EOF +``` + +## Use Cases + +- **Sensitive Data Protection**: Prevent authentication tokens, internal identifiers, or security-related headers from being sent to analytics systems. + +- **Noise Reduction**: Exclude verbose or low-value headers to improve the clarity and usefulness of analytics data. + +- **Compliance and Governance**: Support compliance requirements by ensuring certain headers are never exported outside the platform. + +- **Cost and Storage Optimization**: Reduce analytics payload size by removing unnecessary headers from published events. + + +## Notes + +* Header name matching is case-insensitive. +* The `operation` field is required and must be either `"allow"` or `"deny"`. +* The `headers` array is required but can be empty. When the array is empty, all original headers are included(if allowed explicitly) in analytics for both `"allow"` and `"deny"` modes (safe fallback behavior). +* Request and response headers can use different operation modes independently. +* This policy does not block requests or responses. +* Filtering applies only to analytics collection, not to runtime request handling. +* The policy must be applied per API and does not operate implicitly. + diff --git a/en/docs/ai-gateway/1.2.0/analytics/moesif-analytics.md b/en/docs/ai-gateway/1.2.0/analytics/moesif-analytics.md new file mode 100644 index 000000000..bff1d93c9 --- /dev/null +++ b/en/docs/ai-gateway/1.2.0/analytics/moesif-analytics.md @@ -0,0 +1,134 @@ +--- +title: "Moesif Analytics" +description: "Configure Moesif in API Platform AI Gateway to capture and publish API request and response data." +canonical_url: https://wso2.com/api-platform/docs/ai-gateway/analytics/moesif-analytics/ +md_url: https://wso2.com/api-platform/docs/ai-gateway/analytics/moesif-analytics.md +tags: + - ai-gateway + - analytics + - observability +author: WSO2 API Platform Documentation Team +last_updated: 2026-06-16 +content_type: "how-to" +--- + +# Analytics + +## Overview + +The Analytics feature enables the API Platform to capture, process, and publish API request and response data for observability and business insights. Analytics data is collected asynchronously from the gateway without impacting request latency and is published to an external analytics platform for further analysis and visualization. + +This capability allows platform administrators and business stakeholders to gain visibility into API usage patterns, traffic behavior, latency characteristics, and consumer activity across the platform. + + +## Features + +* Asynchronous collection of API request and response data +* Policy-enriched analytics metadata capture +* Zero impact on request/response latency +* Batched and configurable publishing to external analytics platforms +* Horizontally scalable analytics processing pipeline +* Pluggable publisher model (supports multiple analytics backends) + + +## Prerequisites + + - Active Moesif Account and an Application ID +> **Note:** For obtaining the Application ID: +> - Step 1: Sign up in [Moesif](https://www.moesif.com/) +> - Sept 2: Follow the onboarding wizard. +> - Sept 3: During the sign up process, you will receive a Collector Application ID for your configured application. Copy this value and keep it saved. + +> For more detailed instructions and advanced configuration options, refer to the [official Moesif Documentation](https://www.moesif.com/docs). + + +## Configuration + +Analytics is configured entirely through the gateway `config.toml` file and is enabled at a system level. + +### System Parameters (`config.toml`) + +#### Analytics + +| Parameter | Type | Required | Default | Description | +| --------- | ------- | -------- |-------- | -------------------------------------- | +| `enabled` | boolean | Yes | false | Enables or disables analytics globally | + +#### Publishers + +| Parameter | Type | Required | Description | +| ---------------------- | ------- | -------- | ----------------------------------------- | +| `type` | string | Yes | Analytics publisher type (Currently limited only to ```moesif``` ) | +| `enabled` | boolean | Yes | Enables the publisher | +| `settings` | object | Yes | Map of Publisher specific attributes required for configuring the publisher client | + +#### gRPC Event Server + +This section configures both the Envoy access log streaming settings and the ALS (Access Log Service) server that receives those logs. The ALS server runs within the policy-engine component. + +| Parameter | Type | Required | Default | Description | +| ----------------------- | -------- | -------- |---- | -------------------------------- | +| `buffer_flush_interval` | duration | No | `1000000000`| Maximum time Envoy waits(in nanoseconds) before flushing buffered access log entries.| +| `buffer_size_bytes` | int | No | `16384` | Maximum size of the in-memory buffer used to batch access log entries before sending them to ALS server. | +| `grpc_request_timeout` | duration | No | `20000000000` | Timeout duration Envoy waits(in nanoseconds) for a response from the ALS server before considering the log delivery attempt failed. | +| `server_port` | int | Yes | - | gRPC port on which the ALS server listens for incoming access log streams from Envoy. | +| `shutdown_timeout` | int | No | `600` | Maximum time allowed for the ALS server to gracefully shut down while completing in-flight log processing(in seconds). | +| `als_plain_text` | boolean | No | `true` | Use plaintext gRPC | +| `public_key_path` | string | No | - | Path to the public key used for securing ALS communication when transport-level encryption or authentication is enabled. | +| `private_key_path` | string | No | - | Path to the private key used for securing ALS communication when transport-level encryption or authentication is enabled. | +| `max_message_size` | int | No | `1000000000` |Maximum size of a single gRPC message that the ALS server is allowed to receive from Envoy. | +| `max_header_limit` | int | No | `8192` | Maximum allowed size of request or response headers processed by the ALS server | + +**Note:** The hostname for the ALS connection is automatically derived from the policy-engine configuration. The internal log name identifier is set to `"envoy_access_log"` and is not configurable. + + +## Configuration Examples + +#### Integrate Moesif Publisher + +For Moesif analytics integration, the following publisher-specific attributes must be configured under the `settings` section. These parameters control authentication, batching behavior, and publish intervals for efficient analytics delivery. The required attributes are as follows. + +| Parameter | Type | Required | Description | +| ---------------------- | ------- | -------- | ----------------------------------------- | +| `application_id` | string | Yes | Analytics platform application identifier | +| `publish_interval` | int | Yes | Interval (seconds) between publish cycles | +| `event_queue_size` | int | Yes | Maximum events held in memory | +| `batch_size` | int | Yes | Maximum events per batch | +| `timer_wakeup_seconds` | int | Yes | Publisher timer resolution | + + +```toml +[analytics] +enabled = true + +[[analytics.publishers]] +type = "moesif" +enabled = true + +[analytics.publishers.settings] +application_id = "" +publish_interval = 5 +event_queue_size = 10000 +batch_size = 50 +timer_wakeup_seconds = 3 + +[analytics.grpc_event_server] +buffer_flush_interval = 1000000000 +buffer_size_bytes = 16384 +grpc_request_timeout = 20000000000 +server_port = 18090 +shutdown_timeout = 600 +als_plain_text = true +max_message_size = 1000000000 +max_header_limit = 8192 +``` + + +## Use Cases + +* **API Usage Visibility** – Understand how APIs are consumed across tenants and applications. +* **Operational Insights** – Observe traffic volume, response behavior, and latency trends. +* **Business Intelligence** – Support product and business decisions using API analytics data. +* **Platform Monitoring** – Gain observability into API behavior without impacting performance. + + diff --git a/en/docs/ai-gateway/1.2.0/deployment-modes/immutable-gateway.md b/en/docs/ai-gateway/1.2.0/deployment-modes/immutable-gateway.md new file mode 100644 index 000000000..fae9926a5 --- /dev/null +++ b/en/docs/ai-gateway/1.2.0/deployment-modes/immutable-gateway.md @@ -0,0 +1,171 @@ +--- +title: "Immutable Gateway" +description: "Run API Platform AI Gateway in immutable mode, loading LLM and MCP configurations from files at startup for GitOps workflows." +canonical_url: https://wso2.com/api-platform/docs/ai-gateway/deployment-modes/immutable-gateway/ +md_url: https://wso2.com/api-platform/docs/ai-gateway/deployment-modes/immutable-gateway.md +tags: + - ai-gateway + - deployment + - gitops +author: WSO2 API Platform Documentation Team +last_updated: 2026-06-16 +content_type: "how-to" +--- + +# Immutable Gateway + +This guide explains how to run the API Platform Gateway in **immutable mode**, where API configurations are loaded from files at startup instead of being managed through the REST API. + +Immutable Gateway is the latest evolution of the WSO2 Micro Gateway experience. +If you are coming from WSO2 Micro Gateway `3.2.x`, treat this as the successor track with enhanced API Platform integration, cloud-native deployment patterns, and improved artifact management workflows. + +- Previous generation (reference): [WSO2 Micro Gateway 3.2.x docs](https://mg.docs.wso2.com/en/latest/) +- Current generation: Immutable Gateway V4.0.0 (this documentation set) + +## Overview + +In immutable mode, the gateway controller reads API artifacts from a local directory at startup and applies them automatically. The management REST API remains available for read operations but rejects any mutating requests (`POST`, `PUT`, `PATCH`, `DELETE`) with `405 Method Not Allowed`. + +This is the recommended approach for: + +- **GitOps workflows** — store artifacts in a Git repository and bake them into a custom gateway image via CI/CD. +- **Immutable infrastructure** — ship a self-contained gateway image that requires no runtime configuration. +- **Kubernetes deployments** — mount a ConfigMap or Secret as a volume at the artifacts path. + +## Configuration + +Enable immutable mode in `config.toml`: + +```toml +[immutable_gateway] +enabled = true +``` + +By default, the gateway controller loads artifacts from `/etc/api-platform-gateway/immutable_gateway/artifacts`. You only need to set `artifacts_dir` if you want to use a different path. + +> **Note:** Full configuration reference with all options: +> +> ```toml +> [immutable_gateway] +> enabled = true +> artifacts_dir = "/etc/api-platform-gateway/immutable_gateway/artifacts" +> ``` + +!!! note "Toggling immutable mode from the environment" + Environment variables do not override `config.toml` keys directly — a value reaches a key only through an `env` interpolation token in the config file, and the shipped config carries no token for the `[immutable_gateway]` section. To drive this section from an environment variable, add the tokens to your `config.toml` first: + + {% raw %} + ```toml + [immutable_gateway] + enabled = '{{ env "APIP_GW_IMMUTABLE_GATEWAY_ENABLED" "false" }}' + artifacts_dir = '{{ env "APIP_GW_IMMUTABLE_GATEWAY_ARTIFACTS_DIR" "/etc/api-platform-gateway/immutable_gateway/artifacts" }}' + ``` + {% endraw %} + + then set `APIP_GW_IMMUTABLE_GATEWAY_ENABLED=true`. See [Gateway Configuration and Environment Interpolation](./../setup/configuration.md). + +## Artifact format + +Artifacts use the same Kubernetes-style resource format supported by the gateway. Each file must include `apiVersion`, `kind`, `metadata`, and `spec`. + +### Injecting environment variables + +Artifact files support Go template expressions for injecting dynamic values. Templates are rendered on the raw artifact string before YAML parsing, so expressions work in any string field — `upstream`, `auth`, policy `params`, etc. + +{% raw %} +| Function | Redacted in config dumps | Use for | +|---|---|---| +| `{{ env "KEY" }}` | No | Non-sensitive env vars (URLs, policy param values) | +| `{{ env "KEY" | redact }}` | Yes | Sensitive env vars (tokens, API keys) | +{% endraw %} + +Use `| redact` for sensitive values to hide them from config dumps. A `| default "value"` pipe is available for fallback values. See [Gateway Artifact Templating](../../../api-gateway/1.1.0/setup/artifact-templating.md) for the full function reference. + +### Sample: Reading List API + +Save the following as `artifacts/reading-list-v1.yaml`: + +{% raw %} +```yaml +apiVersion: gateway.api-platform.wso2.com/v1alpha1 +kind: RestApi +metadata: + name: reading-list-api-v1 +spec: + displayName: Reading List API + version: v1.0 + context: /reading-list/$version + upstream: + main: + # read from env, falls back to the public sample backend if not set + url: '{{ env "BACKEND_URL" | default "https://apis.bijira.dev/samples/reading-list-api-service/v1.0" }}' + operations: + - method: GET + path: /books + - method: POST + path: /books + - method: GET + path: /books/{id} + - method: PUT + path: /books/{id} + - method: DELETE + path: /books/{id} +``` +{% endraw %} + +### Directory structure + +The controller walks all subdirectories, so you can organize artifacts however you like: + +``` +artifacts/ +├── rest-apis/ +│ ├── petstore-v1.yaml +│ └── orders-v2.yaml +├── llm-providers/ +│ └── openai.yaml +├── llm-proxies/ +│ └── chat-proxy.yaml +└── mcp-proxies/ + └── tools-proxy.yaml +``` + +## Deployment + +### Baking artifacts into a custom image + +The recommended GitOps approach is to build a custom gateway-controller image with artifacts copied in at build time: + +```dockerfile +FROM ghcr.io/wso2/api-platform/gateway-controller:1.0.0 + +COPY ./artifacts /etc/api-platform-gateway/immutable_gateway/artifacts + +# Bake in a config.toml that sets [immutable_gateway] enabled = true. +COPY ./config.toml /etc/gateway-controller/config.toml +``` + +### Kubernetes — mounting a ConfigMap volume + +You can also mount artifacts as a volume from a ConfigMap or Secret without building a custom image: + +```yaml +volumes: + - name: gateway-artifacts + configMap: + name: gateway-artifacts +volumeMounts: + - name: gateway-artifacts + mountPath: /etc/api-platform-gateway/immutable_gateway/artifacts + readOnly: true +``` + +The mounted `config.toml` must set `[immutable_gateway] enabled = true` — either directly, or by mounting a tokenized `config.toml` (with an `env` token for that section) and setting `APIP_GW_IMMUTABLE_GATEWAY_ENABLED=true` in the container's environment variables (see [Configuration](#configuration)). + +## Invoking the API + +Once the gateway is running with the sample artifact, invoke the Reading List API: + +```bash +curl -i http://localhost:8080/reading-list/v1.0/books +``` diff --git a/en/docs/ai-gateway/1.2.0/deployment-modes/kubernetes/gateway-operator.md b/en/docs/ai-gateway/1.2.0/deployment-modes/kubernetes/gateway-operator.md new file mode 100644 index 000000000..544d8598c --- /dev/null +++ b/en/docs/ai-gateway/1.2.0/deployment-modes/kubernetes/gateway-operator.md @@ -0,0 +1,515 @@ +--- +title: "Kubernetes Operator for API Platform Gateway" +description: "Deploy API Platform AI Gateway on Kubernetes using the Gateway Operator with platform CRDs or the Kubernetes Gateway API." +canonical_url: https://wso2.com/api-platform/docs/ai-gateway/deployment-modes/kubernetes/gateway-operator/ +md_url: https://wso2.com/api-platform/docs/ai-gateway/deployment-modes/kubernetes/gateway-operator.md +tags: + - ai-gateway + - kubernetes + - deployment +author: WSO2 API Platform Documentation Team +last_updated: 2026-06-16 +content_type: "how-to" +--- + +# Kubernetes Operator for API Platform Gateway + +The WSO2 API Platform Gateway Operator enables native Kubernetes deployment using a GitOps-friendly, operator-based model. It manages the full lifecycle of API gateways and REST APIs. You can use **either** platform CRDs **or** the **Kubernetes Gateway API** on the same operator build. + +## Overview + +### Path A — Platform CRDs (`APIGateway` + `RestApi`) + +| CRD | Purpose | +|-----|---------| +| `APIGateway` | Deploys and configures gateway infrastructure (controller, router, policy engine) | +| `RestApi` | Defines API routes, upstreams, and policies | + +The operator watches these CRs, runs Helm for the gateway runtime, and deploys APIs through gateway-controller’s management REST API. + +### Path B — Kubernetes Gateway API (`Gateway` + `HTTPRoute`) + +| Resource | Purpose | +|----------|---------| +| `GatewayClass` | Cluster-scoped class your `Gateway` references (`spec.gatewayClassName` must match the operator allowlist). | +| `Gateway` (`gateway.networking.k8s.io`) | Triggers the same Helm-based gateway deployment as `APIGateway`; controller endpoint is registered for discovery by routes. | +| `HTTPRoute` | Parents attach to a `Gateway`; `backendRefs` target a Kubernetes `Service`. The operator maps the route to `APIConfigData` and calls gateway-controller **`/api/management/v0.9/rest-apis`** (same outcome as `RestApi`, different user surface). | +| `APIPolicy` (optional) | Rule or API-level policies for Gateway API flows; same CRD as HTTPRoute policy demos in-repo. | + +**Hands-on walkthrough:** manifests are in **[Kubernetes Gateway API path](#kubernetes-gateway-api-path)** below. + +## Prerequisites + +- Kubernetes cluster (Docker Desktop, Kind, Minikube, OpenShift, etc.) +- `kubectl` installed +- `helm` v3+ +- `jq` (for JSON output) + +## Installation + +### 1. Install Cert-Manager + +The operator requires cert-manager for TLS certificate management: + +```sh +helm upgrade --install \ + cert-manager oci://quay.io/jetstack/charts/cert-manager \ + --version v1.19.1 \ + --namespace cert-manager \ + --create-namespace \ + --set crds.enabled=true \ + --debug --wait --timeout 10m +``` + +### 2. Install Gateway Operator + +```sh +helm install my-gateway-operator oci://ghcr.io/wso2/api-platform/helm-charts/gateway-operator --version 0.6.0 +``` + +## Deploying an API Gateway + +Create an `APIGateway` resource to bootstrap gateway components: + +```yaml +apiVersion: gateway.api-platform.wso2.com/v1alpha1 +kind: APIGateway +metadata: + name: cluster-gateway +spec: + gatewayClassName: "production" + + apiSelector: + scope: Cluster # Accepts APIs from any namespace + + infrastructure: + replicas: 1 + resources: + requests: + cpu: "500m" + memory: "1Gi" + limits: + cpu: "2" + memory: "4Gi" + + controlPlane: + host: "gateway-control-plane.gateway-operator-system.svc.cluster.local:8443" + tls: + enabled: true + + storage: + type: sqlite + + configRef: + name: gateway-custom-config # Optional: reference a ConfigMap with custom Helm values +``` + +Apply the sample APIGateway: + +```sh +kubectl apply -f https://raw.githubusercontent.com/wso2/api-platform/refs/heads/main/kubernetes/gateway-operator/config/samples/api_v1_apigateway.yaml + +kubectl get apigateway -n default -o json | jq '.items[0].status' +``` + +## Deploying REST APIs + +Define APIs using the `RestApi` custom resource: + +```yaml +apiVersion: gateway.api-platform.wso2.com/v1alpha1 +kind: RestApi +metadata: + name: my-api + labels: + environment: "production" +spec: + displayName: My API + version: v1.0 + context: /test + upstream: + main: + url: https://httpbin.org/anything + operations: + - method: GET + path: /info + - method: POST + path: /submit +``` + +Apply the sample RestApi: + +```sh +kubectl apply -f https://raw.githubusercontent.com/wso2/api-platform/refs/heads/main/kubernetes/gateway-operator/config/samples/api_v1_restapi.yaml + +kubectl get restapi -n default -o json | jq '.items[0].status' +``` + +### Test API Endpoints + +**`RestApi` / APIGateway-managed API** (example context `/test`, operation `GET /info`): + +```sh +curl https://localhost:8443/test/info -vk +``` + +## Kubernetes Gateway API path + +Use this when you prefer standard Gateway API resources instead of `APIGateway` / `RestApi`. The manifests below match the **`gateway-api-demo`** demo in this repository (`kubernetes/helm/resources/gateway-api-operator-demo/`). Apply them **in order**, or concatenate and `kubectl apply -f -`. + +### What you need + +- **Gateway Operator** Helm install with RBAC for `gateway.networking.k8s.io` (included in the operator chart). +- **Gateway API CRDs** in the cluster (cloud add-on, another controller, or `--set gatewayApi.installStandardCRDs=true` on a greenfield cluster where no conflicting CRD owner exists). +- **`GatewayClass`** whose `metadata.name` is listed in **`gatewayApi.managedGatewayClassNames`** (default includes `wso2-api-platform`). +- **`spec.controllerName`** on the `GatewayClass` should match the operator (`gateway.api-platform.wso2.com/gateway-operator`) so the operator can set **`Accepted`** status on the class. +- **cert-manager** if you add **Certificate** / **Issuer** via per-Gateway Helm values (not included in the minimal YAMLs below; extend with a `ConfigMap` and **`gateway.api-platform.wso2.com/helm-values-configmap`** on the `Gateway` when needed). +- A **`Service`** backend referenced from **`HTTPRoute.spec.rules[].backendRefs`**. + +### 1. Namespace + +```yaml +apiVersion: v1 +kind: Namespace +metadata: + name: gateway-api-demo + labels: + app.kubernetes.io/part-of: gateway-api-operator-demo +``` + +### 2. GatewayClass + +```yaml +# GatewayClass must use controllerName matching the operator so the operator can set status.conditions[Accepted]. +apiVersion: gateway.networking.k8s.io/v1 +kind: GatewayClass +metadata: + name: wso2-api-platform +spec: + controllerName: gateway.api-platform.wso2.com/gateway-operator +``` + +### 3. Gateway + +!!! warning "Pre-create the encryption key Secret" + At-rest encryption is mandatory and fail-closed. The operator installs the gateway into the **Gateway's own namespace**, so the AES-256 key Secret must exist in that namespace (`gateway-api-demo` below) before you apply the `Gateway`, and the per-Gateway values must enable it (`gateway.controller.encryptionKeys.enabled: true` with a `secretName`). Create it as shown in [Security Hardening → Encryption Keys](https://wso2.com/api-platform/docs/api-gateway/deployment/production-deployment/security-hardening/#encryption-keys), using `-n gateway-api-demo`. + +```yaml +# Per-Gateway Helm values enabling at-rest encryption (mandatory, fail-closed). +# Referenced by the Gateway below via the helm-values-configmap annotation. +apiVersion: v1 +kind: ConfigMap +metadata: + name: platform-gw-values + namespace: gateway-api-demo +data: + # The operator loads ConfigMap.data["values.yaml"] and merges it into the gateway Helm values. + values.yaml: | + gateway: + controller: + encryptionKeys: + enabled: true + secretName: gateway-encryption-keys +--- +# Triggers the operator: Helm installs release named platform-gw-gateway, then registers the gateway-controller Service. +apiVersion: gateway.networking.k8s.io/v1 +kind: Gateway +metadata: + name: platform-gw + namespace: gateway-api-demo + annotations: + # Per-Gateway Helm overrides (here: mandatory at-rest encryption). ConfigMap key must be values.yaml. + gateway.api-platform.wso2.com/helm-values-configmap: platform-gw-values + # Prevent this Gateway from matching RestApi CRs intended for APIGateway (CRD mode) in mixed demos. + gateway.api-platform.wso2.com/api-selector: '{"scope":"LabelSelector","matchLabels":{"gateway.api-platform.wso2.com/restapi-target":"k8s"}}' + labels: + app.kubernetes.io/part-of: gateway-api-operator-demo +spec: + gatewayClassName: wso2-api-platform + infrastructure: + labels: + environment: dev + team: platform + annotations: + prometheus.io/scrape: "true" + listeners: + - name: http + port: 8080 + protocol: HTTP + allowedRoutes: + namespaces: + from: Same + - name: https + port: 8443 + protocol: HTTPS + allowedRoutes: + namespaces: + from: Same +``` + +### 4. Sample backend (Deployment + Service) + +```yaml +apiVersion: apps/v1 +kind: Deployment +metadata: + name: hello-backend + namespace: gateway-api-demo + labels: + app: hello-backend + app.kubernetes.io/part-of: gateway-api-operator-demo +spec: + replicas: 1 + selector: + matchLabels: + app: hello-backend + template: + metadata: + labels: + app: hello-backend + spec: + containers: + - name: sample-backend + image: ghcr.io/wso2/api-platform/sample-service:latest + args: + - "-addr" + - ":9080" + - "-pretty" + ports: + - name: http + containerPort: 9080 + resources: + requests: + cpu: 10m + memory: 32Mi +--- +apiVersion: v1 +kind: Service +metadata: + name: hello-backend + namespace: gateway-api-demo + labels: + app.kubernetes.io/part-of: gateway-api-operator-demo +spec: + type: ClusterIP + selector: + app: hello-backend + ports: + - name: http + port: 9080 + targetPort: 9080 +``` + +Wait until the **Gateway** is **Programmed** and gateway workloads are **Ready**, then apply the HTTPRoute(s). + +### 5. HTTPRoute (`hello-api`) + +```yaml +# Operator maps this route to APIConfigData and calls gateway-controller /api/management/v0.9/rest-apis. +# Default REST handle is namespace-name: gateway-api-demo-hello-api (override with gateway.api-platform.wso2.com/api-handle). +apiVersion: gateway.networking.k8s.io/v1 +kind: HTTPRoute +metadata: + name: hello-api + namespace: gateway-api-demo + labels: + app.kubernetes.io/part-of: gateway-api-operator-demo + annotations: + gateway.api-platform.wso2.com/api-version: "v1.0" + gateway.api-platform.wso2.com/context: "/hello-context" + gateway.api-platform.wso2.com/display-name: "Hello API" +spec: + parentRefs: + - group: gateway.networking.k8s.io + kind: Gateway + name: platform-gw + namespace: gateway-api-demo + hostnames: + - demo.gateway-api.local + rules: + - matches: + # match.method is optional; if omitted, the operator emits GET, POST, PUT, PATCH, DELETE, HEAD, OPTIONS for this path. + - path: + type: PathPrefix + value: /hello + method: GET + backendRefs: + - group: "" + kind: Service + name: hello-backend + port: 9080 + weight: 1 +``` + +### 6. Optional: second HTTPRoute (`hello-api-2`) + +```yaml +apiVersion: gateway.networking.k8s.io/v1 +kind: HTTPRoute +metadata: + name: hello-api-2 + namespace: gateway-api-demo + labels: + app.kubernetes.io/part-of: gateway-api-operator-demo + annotations: + gateway.api-platform.wso2.com/display-name: "Hello API 2" +spec: + parentRefs: + - group: gateway.networking.k8s.io + kind: Gateway + name: platform-gw + namespace: gateway-api-demo + hostnames: + - demo.gateway-api.local + rules: + - matches: + - path: + type: PathPrefix + value: /hello + backendRefs: + - group: "" + kind: Service + name: hello-backend + port: 9080 + weight: 1 +``` + +Verify: `kubectl get gateway,httproute -n gateway-api-demo`, wait for parent conditions on the HTTPRoute, then exercise the API (port-forward or in-cluster curl to **gateway-runtime** HTTPS as in **Testing APIs** below). + +### HTTPRoute annotations (payload metadata) + +Common annotations on `HTTPRoute` are copied into the **`api.yaml`** payload (for example **`gateway.api-platform.wso2.com/context`**, **`api-version`**, **`api-handle`**, **`display-name`**, **`project-id`**). If **`context`** is omitted or only whitespace, it defaults to **`/`**. If a rule **`match`** omits **`method`**, the operator emits all RestApi-supported verbs for that path: GET, POST, PUT, PATCH, DELETE, HEAD, OPTIONS. + +### Mixed clusters (`RestApi` + `Gateway`) + +If you run **both** `APIGateway`-selected **`RestApi`** resources and **Gateway API** routes, keep the **`gateway.api-platform.wso2.com/api-selector`** annotation on the **`Gateway`** (as in the YAML above) so this gateway does not select `RestApi` CRs meant for another `APIGateway`. + +### Test API Endpoints + +**Kubernetes Gateway API** — HTTPRoute **`hello-api`** from [above](#5-httproute-hello-api): API **`context`** `/hello-context`, route match path prefix **`/hello`** (hits Envoy HTTPS on the forwarded router port): + +```sh +curl --request GET \ + --url 'https://localhost:8443/hello-context/hello' \ + --header 'Accept: application/json' \ + -k +``` + +Use **`NS=gateway-api-demo`** in the port-forward snippet when testing that demo. The sample backend may respond with a short plain-text body (e.g. `hello from gateway api demo`) depending on chart and image version. + +## Adding Backend Certificates + +For APIs connecting to backends with self-signed certificates: + +### 1. Download the Certificate + +```sh +curl -X GET "https://raw.githubusercontent.com/wso2/api-platform/refs/heads/main/gateway/resources/secure-backend/test-backend-certs/test-backend.crt" \ + -o /tmp/test-backend.crt +``` + +### 2. Add Certificate to Gateway + +The management API uses basic auth with the credentials from your Helm values +(`controller.auth.basic.users`; the chart default is `admin` / `admin`). Export them, changing them if +you overrode the chart defaults: + +```sh +export ADMIN_USERNAME=admin +export ADMIN_PASSWORD=admin +``` + +```sh +cert_path="/tmp/test-backend.crt" +curl -X POST http://localhost:9090/api/management/v0.9/certificates -u "$ADMIN_USERNAME:$ADMIN_PASSWORD" \ + -H "Content-Type: application/json" \ + -d "{\"certificate\":$(jq -Rs . < $cert_path),\"filename\":\"my-cert.pem\", \"name\":\"test\"}" +``` + +## Custom Configuration + +Per-gateway Helm values are supplied as a **ConfigMap** whose data includes **`values.yaml`** (partial YAML is fine; the operator **deep-merges** it onto the operator’s default gateway values file loaded from **`gateway.helm.valuesFilePath`**). + +### `APIGateway` (`spec.configRef`) + +Create the ConfigMap: + +```yaml +apiVersion: v1 +kind: ConfigMap +metadata: + name: gateway-custom-config +data: + values.yaml: | + ### IMPORTANT you need to provide entire values yaml of the gateway helm. + gateway: + controller: + logging: + level: debug + router: + service: + type: LoadBalancer +``` + +Reference it from the **APIGateway**: + +```yaml +spec: + configRef: + name: gateway-custom-config +``` + +### Kubernetes Gateway API (`Gateway`) + +Use the **same ConfigMap** shape (`data.values.yaml`). Put the ConfigMap in the **same namespace** as the **`Gateway`**, then point the **`Gateway`** at it with this annotation (not a field on **`spec`**): + +```yaml +apiVersion: gateway.networking.k8s.io/v1 +kind: Gateway +metadata: + name: platform-gw + namespace: gateway-api-demo + annotations: + gateway.api-platform.wso2.com/helm-values-configmap: gateway-custom-config + # ... other annotations (e.g. api-selector) as needed +spec: + gatewayClassName: wso2-api-platform + # listeners, infrastructure, ... +``` + +The operator reads **`metadata.annotations[gateway.api-platform.wso2.com/helm-values-configmap]`**, loads **`ConfigMap.data["values.yaml"]`**, and merges it into the Helm values used for **`{metadata.name}-gateway`**, same merge rules as **`APIGateway.spec.configRef`**. + +## Architecture + +``` +┌─────────────────────────────────────────────────────────────────┐ +│ Gateway Operator │ +│ Watches: APIGateway, RestApi; Gateway, HTTPRoute (+ Service, │ +│ APIPolicy, Secret, ConfigMap for Gateway API path) │ +└─────────────────────────────────────────────────────────────────┘ + │ + ▼ +┌─────────────────────────────────────────────────────────────────┐ +│ Gateway Components │ +│ ┌─────────────────┐ ┌────────┐ ┌──────────────────┐ │ +│ │ Gateway │ │ Router │ │ Policy Engine │ │ +│ │ Controller │ │(Envoy) │ │ │ │ +│ │ (Control Plane) │ │ │ │ │ │ +│ └─────────────────┘ └────────┘ └──────────────────┘ │ +└─────────────────────────────────────────────────────────────────┘ +``` + +- **CRD path:** `APIGateway` drives Helm; `RestApi` drives management REST deploys. +- **Gateway API path:** `Gateway` drives the same Helm install pattern; `HTTPRoute` is translated to the same management REST payload shape as `RestApi`. + +## Default Ports + +| Port | Component | Description | +|------|-----------|-------------| +| 9090 | Controller | REST API for management | +| 18000 | Controller | xDS gRPC for Envoy | +| 18001 | Controller | Policy xDS | +| 8080 | Router | HTTP traffic | +| 8443 | Router | HTTPS traffic | +| 9901 | Router | Envoy admin | +| 9001 | Policy Engine | ext_proc gRPC | diff --git a/en/docs/ai-gateway/1.2.0/deployment-modes/kubernetes/kubernetes-standalone.md b/en/docs/ai-gateway/1.2.0/deployment-modes/kubernetes/kubernetes-standalone.md new file mode 100644 index 000000000..cf04fe07c --- /dev/null +++ b/en/docs/ai-gateway/1.2.0/deployment-modes/kubernetes/kubernetes-standalone.md @@ -0,0 +1,309 @@ +--- +title: "Kubernetes Standalone Mode" +description: "Install and manage API Platform AI Gateway on Kubernetes using the standalone Helm chart without the Gateway Operator." +canonical_url: https://wso2.com/api-platform/docs/ai-gateway/deployment-modes/kubernetes/kubernetes-standalone/ +md_url: https://wso2.com/api-platform/docs/ai-gateway/deployment-modes/kubernetes/kubernetes-standalone.md +tags: + - ai-gateway + - kubernetes + - deployment +author: WSO2 API Platform Documentation Team +last_updated: 2026-06-16 +content_type: "how-to" +--- + +# API Platform Gateway - Kubernetes Standalone Mode + +This guide explains how to run API Platform Gateway in **Standalone Mode** using the gateway Helm chart only (without the Gateway Operator). + +Standalone mode is recommended when you want: + +- Direct Helm-based lifecycle management of gateway components. +- A simpler footprint without operator-managed CRDs. +- Explicit control over values and release upgrades. + +For mode selection and architecture context, see [API Platform Kubernetes Gateway deployment modes](./overview.md). + +## What Gets Deployed + +The gateway chart deploys the runtime components used by API Platform Gateway (controller and gateway runtime workloads) from chart templates and values. + +Chart reference: + +- OCI chart: `oci://ghcr.io/wso2/api-platform/helm-charts/gateway` +- Local chart (repo): `kubernetes/helm/gateway-helm-chart` + +## Prerequisites + +- Kubernetes `1.24+` +- Helm `3.12+` +- `kubectl` +- `cert-manager` (required for cert-manager-backed TLS flows) + +## Install cert-manager + +```bash +helm repo add jetstack https://charts.jetstack.io --force-update +helm repo update + +helm install cert-manager jetstack/cert-manager \ + --namespace cert-manager \ + --create-namespace \ + --set crds.enabled=true +``` + +Verify: + +```bash +kubectl get pods -n cert-manager +``` + +## Create the Encryption Key Secret + +At-rest encryption is **mandatory and fail-closed** - the chart refuses to render without an AES-256 key Secret. Create it in the namespace you install into, **before** installing the chart. + +```bash +openssl rand 32 > default-aesgcm256-v1.bin +kubectl create secret generic gateway-encryption-keys \ + --from-file=default-aesgcm256-v1.bin=default-aesgcm256-v1.bin && \ + rm default-aesgcm256-v1.bin # remove the plaintext key only after the Secret is created +# For a non-default namespace, first `kubectl create namespace `, +# then add `-n ` to both this command and `helm install`. +``` + +The Secret's key entry must be named `default-aesgcm256-v1.bin`. See [Security Hardening → Encryption Keys](https://wso2.com/api-platform/docs/api-gateway/deployment/production-deployment/security-hardening/#encryption-keys) for key rotation and multi-key setups. + +## Install Gateway Chart + +Use one of the following patterns. + +### Default install + +```bash +helm install ap-gateway oci://ghcr.io/wso2/api-platform/helm-charts/gateway \ + --set gateway.controller.encryptionKeys.enabled=true \ + --set gateway.controller.encryptionKeys.secretName=gateway-encryption-keys +``` + +### Install into a dedicated namespace + +```bash +kubectl create namespace api-gateway + +helm install ap-gateway oci://ghcr.io/wso2/api-platform/helm-charts/gateway \ + --namespace api-gateway \ + --set gateway.controller.encryptionKeys.enabled=true \ + --set gateway.controller.encryptionKeys.secretName=gateway-encryption-keys +``` + +### Install with control-plane overrides + +```bash +helm install ap-gateway oci://ghcr.io/wso2/api-platform/helm-charts/gateway \ + --set gateway.controller.controlPlane.host="platform.example.com" \ + --set gateway.controller.controlPlane.port=8443 \ + --set gateway.controller.controlPlane.token.value="your-token-here" \ + --set gateway.controller.encryptionKeys.enabled=true \ + --set gateway.controller.encryptionKeys.secretName=gateway-encryption-keys +``` + +### Install with a values file + +`custom-values.yaml` must define the mandatory encryption settings: + +```yaml +gateway: + controller: + encryptionKeys: + enabled: true + secretName: gateway-encryption-keys +``` + +```bash +helm install ap-gateway oci://ghcr.io/wso2/api-platform/helm-charts/gateway \ + -f custom-values.yaml +``` + +## Verify Installation + +```bash +helm status ap-gateway +kubectl get all -l app.kubernetes.io/instance=ap-gateway +``` + +Check logs: + +```bash +# Controller logs +kubectl logs -l app.kubernetes.io/component=controller + +# Gateway runtime logs +kubectl logs -l app.kubernetes.io/component=gateway-runtime +``` + +## Upgrade and Uninstall + +Upgrade: + +```bash +helm upgrade ap-gateway oci://ghcr.io/wso2/api-platform/helm-charts/gateway -f custom-values.yaml +``` + +Uninstall: + +```bash +helm uninstall ap-gateway +``` + +Namespace-scoped uninstall: + +```bash +helm uninstall ap-gateway --namespace api-gateway +``` + +## Core Configuration Areas + +Most runtime configuration is controlled in `values.yaml`. Common sections: + +- `gateway.controller.image`, `gateway.gatewayRuntime.image` +- `gateway..deployment.*` +- `gateway..service.*` +- `gateway.controller.controlPlane.*` +- `gateway.controller.logging.*` +- `gateway.controller.tls.*` +- `gateway.controller.upstreamCerts.*` +- `gateway.config.policy_engine.*` + +Refer to inline comments in chart `values.yaml` for all supported fields. + +## TLS Configuration + +### Option 1: cert-manager (recommended) + +```bash +helm install ap-gateway oci://ghcr.io/wso2/api-platform/helm-charts/gateway \ + --set gateway.controller.tls.enabled=true +``` + +Production-style example: + +```yaml +gateway: + controller: + tls: + enabled: true + certificateProvider: cert-manager + certManager: + createIssuer: false + issuerRef: + name: letsencrypt-prod + kind: Issuer + commonName: api.example.com + dnsNames: + - api.example.com + - "*.api.example.com" +``` + +### Option 2: Existing TLS secret + +```bash +kubectl create secret tls gateway-tls \ + --cert=path/to/tls.crt \ + --key=path/to/tls.key + +helm install ap-gateway oci://ghcr.io/wso2/api-platform/helm-charts/gateway \ + --set gateway.controller.tls.enabled=true \ + --set gateway.controller.tls.certificateProvider=secret \ + --set gateway.controller.tls.secret.name=gateway-tls +``` + +## Upstream Custom CAs + +When calling upstream services that use private/self-signed CAs: + +```bash +kubectl create secret generic upstream-ca-certs \ + --from-file=ca1.crt=path/to/ca1.crt \ + --from-file=ca2.crt=path/to/ca2.crt + +helm install ap-gateway oci://ghcr.io/wso2/api-platform/helm-charts/gateway \ + --set gateway.controller.upstreamCerts.enabled=true \ + --set gateway.controller.upstreamCerts.secretName=upstream-ca-certs +``` + +## Create and Invoke API + +### Port-forward Gateway Controller Service + +```bash +kubectl port-forward svc/ap-gateway-controller 9090:9090 +``` + +### Verify gateway controller admin endpoint is running +```bash +curl http://localhost:9094/api/admin/v0.9/health +``` + +### Deploy an API configuration + +The management API uses basic auth with the credentials from your Helm values +(`controller.auth.basic.users`; the chart default is `admin` / `admin`). Export them, changing them if +you overrode the chart defaults: + +```bash +export ADMIN_USERNAME=admin +export ADMIN_PASSWORD=admin +``` + +```bash +curl -X POST http://localhost:9090/api/management/v0.9/rest-apis \ + -u "$ADMIN_USERNAME:$ADMIN_PASSWORD" \ + -H "Content-Type: application/yaml" \ + --data-binary @- <<'EOF' +apiVersion: gateway.api-platform.wso2.com/v1alpha1 +kind: RestApi +metadata: + name: reading-list-api-v1.0 +spec: + displayName: Reading-List-API + version: v1.0 + context: /reading-list/$version + upstream: + main: + url: https://apis.bijira.dev/samples/reading-list-api-service/v1.0 + policies: + - name: set-headers + version: v1 + params: + request: + headers: + - name: x-wso2-apip-gateway-version + value: v1.0.0 + response: + headers: + - name: x-environment + value: development + operations: + - method: GET + path: /books + - method: POST + path: /books + - method: GET + path: /books/{id} + - method: PUT + path: /books/{id} + - method: DELETE + path: /books/{id} +EOF +``` + +### Test routing through the gateway +```bash +curl http://localhost:8080/reading-list/v1.0/books +curl -k https://localhost:8443/reading-list/v1.0/books +``` + +## Next Steps + +- For operator-managed lifecycle and CRDs, see [Kubernetes Operator deployment mode](./gateway-operator.md). +- For mode comparison and migration context from Kubernetes Gateway 1.3.0, see the [deployment modes overview page](./overview.md). \ No newline at end of file diff --git a/en/docs/ai-gateway/1.2.0/deployment-modes/kubernetes/overview.md b/en/docs/ai-gateway/1.2.0/deployment-modes/kubernetes/overview.md new file mode 100644 index 000000000..4c895c683 --- /dev/null +++ b/en/docs/ai-gateway/1.2.0/deployment-modes/kubernetes/overview.md @@ -0,0 +1,104 @@ +--- +title: "AI Gateway Kubernetes Deployment Modes" +description: "Choose between standalone and operator-managed Kubernetes deployment modes for API Platform AI Gateway." +canonical_url: https://wso2.com/api-platform/docs/ai-gateway/deployment-modes/kubernetes/overview/ +md_url: https://wso2.com/api-platform/docs/ai-gateway/deployment-modes/kubernetes/overview.md +tags: + - ai-gateway + - kubernetes + - deployment +author: WSO2 API Platform Documentation Team +last_updated: 2026-06-19 +content_type: "overview" +--- + +# API Platform Kubernetes Gateway (V2.0.0) + +API Platform Gateway is the latest evolution of the WSO2 Kubernetes gateway(APK) experience. +If you are coming from WSO2 Kubernetes Gateway `1.3.0`, treat this as the successor track with a broader API Platform integration model and improved Kubernetes-native workflows. + +- Previous generation (reference): [WSO2 APK 1.3.0 docs](https://apk.docs.wso2.com/en/latest/) +- Current generation: API Platform Kubernetes Gateway V2.0.0 (this documentation set) + +## What Is New in V2.0.0 + +- Unified API Platform gateway model for both standalone and operator-driven deployments. +- Native support for Kubernetes Operator workflows and GitOps-style resource management. +- Flexible API exposure model: + - API Platform CRDs (`APIGateway`, `RestApi`, and related CRDs) + - Kubernetes Gateway API (`GatewayClass`, `Gateway`, `HTTPRoute`, optional `APIPolicy`) +- Improved extensibility for policies and secret/config-based parameter references. + +## Benefits of API Platform Kubernetes Gateway V2.0.0 + +### Unified API Platform Model + +- Single gateway model covering both standalone and operator-driven deployments, removing the fragmentation from the APK 1.3.0 era. + +### Kubernetes-Native Workflows + +- Native Kubernetes Operator support with declarative CRDs (`APIGateway`, `RestApi`, `Gateway`, `HTTPRoute`). + +### Flexible API Exposure Model + +- Supports both WSO2-native CRDs and the standard Kubernetes Gateway API, avoiding vendor lock-in. + +### Extensible Policy Engine + +- Policies can be attached to APIs in either deployment mode. +- Supports authentication, rate limiting, header manipulation, and custom request/response mediation. +- Dual-language policy development: Go for performance-critical logic, Python for AI/ML and data transformations. + +### High Availability and Production-Ready + +- Controller uses PostgreSQL for persistent, shared API configuration storage. +- Runtime uses Redis for distributed rate limiting across replicas. + +### Built-In Observability + +- Metrics (Prometheus/Grafana), distributed tracing (Jaeger), logging, and Moesif analytics integration. + +### Improved Extensibility via Custom Policies and Secrets + +- Custom gateway images built with the `ap` CLI. +- Secrets and config-based parameter references for improved security posture. + +## Deployment Modes + +Choose one of the following modes based on your operational model. + +### 1) Kubernetes Standalone Mode + +Use this mode when you only need the gateway runtime and manage it directly via the gateway Helm chart. + +- Installs gateway components without the gateway operator. +- Best for simpler setups or environments where operator lifecycle management is not required. +- Guide: [`kubernetes-standalone.md`](./kubernetes-standalone.md) + +### 2) Kubernetes Operator Mode + +Use this mode when you want operator-managed lifecycle, declarative resources, and automation. + +- Installs the Gateway Operator via Helm. +- Supports: + - API Platform CRD path (`APIGateway` + `RestApi`) + - Kubernetes Gateway API path (`Gateway` + `HTTPRoute`) +- Guide: [`gateway-operator.md`](./gateway-operator.md) + +## How to Choose a Mode + +- Choose **Standalone Mode** if your priority is a lightweight gateway runtime install with Helm-managed values. +- Choose **Operator Mode** if your priority is Kubernetes-native reconciliation, GitOps workflows, multi-resource lifecycle automation, and CRD-based API management. + +## Policy Configuration + +API Platform Kubernetes Gateway V2.0.0 supports attaching policies to APIs deployed in either mode. Policies control request/response mediation, rate limiting, authentication, and other gateway behaviors. + +For a full reference on available policies and how to configure them, see [Policy Configuration Overview](https://wso2.com/api-platform/docs/api-gateway/policies/overview/). + +## Next Steps + +Learn more about the deployment modes: + +- [Standalone mode](./kubernetes-standalone.md) +- [Kubernetes Operator mode](./gateway-operator.md) diff --git a/en/docs/ai-gateway/1.2.0/llm-proxy/guardrails/aws-bedrock-guardrail.md b/en/docs/ai-gateway/1.2.0/llm-proxy/guardrails/aws-bedrock-guardrail.md new file mode 100644 index 000000000..d1e1d9345 --- /dev/null +++ b/en/docs/ai-gateway/1.2.0/llm-proxy/guardrails/aws-bedrock-guardrail.md @@ -0,0 +1,291 @@ +--- +title: "AWS Bedrock Guardrail" +description: "Validate LLM request and response content against AWS Bedrock Guardrails for content filtering, topic detection, and PII masking." +canonical_url: https://wso2.com/api-platform/docs/ai-gateway/llm-proxy/guardrails/aws-bedrock-guardrail/ +md_url: https://wso2.com/api-platform/docs/ai-gateway/llm-proxy/guardrails/aws-bedrock-guardrail.md +tags: + - ai-gateway + - guardrails + - aws +author: WSO2 API Platform Documentation Team +last_updated: 2026-06-16 +content_type: "reference" +--- + +# AWS Bedrock Guardrail + +## Overview + +The AWS Bedrock Guardrail policy validates request or response body content against AWS Bedrock Guardrails, which provide enterprise-grade content filtering, topic detection, word filtering, and PII (Personally Identifiable Information) detection and masking. This guardrail enables you to enforce content safety policies consistently across your LLM applications using AWS Bedrock's managed guardrail service. + +The policy supports multiple authentication modes including AWS IAM role assumption, static credentials, and default credential chain, making it flexible for various AWS deployment scenarios. It can mask or redact PII entities in requests and restore them in responses, ensuring data privacy while maintaining functionality. + +## Features + +- **Content filtering**: Detects and blocks prohibited content based on guardrail policies +- **Topic detection**: Validates content against configured topic restrictions +- **Word filtering**: Blocks content containing prohibited words or phrases +- **PII detection and masking**: Identifies and masks PII entities (emails, phone numbers, SSNs, etc.) +- **PII restoration**: Restores masked PII in responses when configured (masking mode) +- **PII redaction**: Permanently removes PII by replacing with "*****" (redaction mode) +- **Multiple authentication modes**: Supports role assumption, static credentials, or default AWS credential chain +- **JSONPath support**: Extract and validate specific fields within JSON payloads +- **Separate request/response configuration**: Independent configuration for request and response phases +- **Detailed assessment information**: Optional detailed violation information in error responses + +## Configuration + +### Parameters + +#### Request Phase + +| Parameter | Type | Required | Default | Description | +|-----------|------|----------|---------|-------------| +| `jsonPath` | string | No | `""` | JSONPath expression to extract a specific value from JSON payload. If empty, validates the entire payload as a string. | +| `redactPII` | boolean | No | `false` | If `true`, redacts PII by replacing with "*****" (permanent). If `false`, masks PII with placeholders that can be restored in responses. | +| `passthroughOnError` | boolean | No | `false` | If `true`, allows requests to proceed if AWS Bedrock Guardrail API call fails. If `false`, blocks requests on API errors. | +| `showAssessment` | boolean | No | `false` | If `true`, includes detailed assessment information from AWS Bedrock Guardrail in error responses. | + +#### Response Phase + +| Parameter | Type | Required | Default | Description | +|-----------|------|----------|---------|-------------| +| `jsonPath` | string | No | `""` | JSONPath expression to extract a specific value from JSON payload. If empty, validates the entire payload as a string. | +| `passthroughOnError` | boolean | No | `false` | If `true`, allows requests to proceed if AWS Bedrock Guardrail API call fails. If `false`, blocks requests on API errors. | +| `showAssessment` | boolean | No | `false` | If `true`, includes detailed assessment information from AWS Bedrock Guardrail in error responses. | + +### System Parameters (Required) + +These parameters are typically configured at the gateway level and automatically injected, or you can override those values from the params section in the api artifact definition file as well: + +| Parameter | Type | Required | Description | +|-----------|------|----------|-------------| +| `region` | string | Yes | AWS region where the Bedrock Guardrail is located (e.g., "us-east-1", "us-west-2"). | +| `guardrailID` | string | Yes | AWS Bedrock Guardrail identifier (the unique ID of your guardrail). | +| `guardrailVersion` | string | Yes | AWS Bedrock Guardrail version (e.g., "DRAFT", "1", "2"). Use "DRAFT" for testing, numbered versions for production. | +| `awsAccessKeyID` | string | No | AWS access key ID (for static credentials or role assumption). If omitted, runtime uses default AWS credential chain (environment variables, IAM roles, etc.). | +| `awsSecretAccessKey` | string | No | AWS secret access key (for static credentials or role assumption). If omitted, runtime uses default AWS credential chain. | +| `awsSessionToken` | string | No | AWS session token (optional, for temporary credentials). | +| `awsRoleARN` | string | No | AWS IAM role ARN to assume (for role-based authentication). If specified, runtime assumes this role instead of using static credentials. | +| `awsRoleRegion` | string | No | AWS region for role assumption (required if `awsRoleARN` is specified). | +| `awsRoleExternalID` | string | No | External ID for role assumption (optional, for cross-account access security). | + + +### Configuring System Parameters in config.toml + +System parameters can be configured globally in the gateway's `config.toml` file. These values serve as defaults for all AWS Bedrock Guardrail policy instances and can be overridden per-policy in the API configuration if needed. + +#### Location in config.toml + +Add the following configuration section to your `config.toml` file: + +```toml +awsbedrock_guardrail_region = "us-east-1" +awsbedrock_guardrail_id = "your-guardrail-id" +awsbedrock_guardrail_version = "DRAFT" +awsbedrock_access_key_id = "" +awsbedrock_secret_access_key = "" +awsbedrock_session_token = "" +awsbedrock_role_arn = "" +awsbedrock_role_region = "" +awsbedrock_role_external_id = "" +``` + +## JSONPath Support + +The guardrail supports JSONPath expressions to extract and validate specific fields within JSON payloads. Common examples: + +- `$.message` - Extracts the `message` field from the root object +- `$.data.content` - Extracts nested content from `data.content` +- `$.items[0].text` - Extracts text from the first item in an array +- `$.messages[0].content` - Extracts content from the first message in a messages array +- `$.messages[-1].content` - Extracts content from the last message in a messages array + +If `jsonPath` is empty or not specified, the entire payload is treated as a string and validated. + +## PII Handling + +### Masking Mode (redactPII: false) + +When `redactPII` is `false`: +- **Request phase**: PII entities are masked with placeholders like `EMAIL_0001`, `PHONE_0002`, etc. +- Use this mode when you need PII to flow through the system but want it masked during processing + +### Redaction Mode (redactPII: true) + +When `redactPII` is `true`: +- PII entities are permanently replaced with `*****` +- Original values cannot be restored +- Use this mode when you want to completely remove PII from content + +## Examples + +### Example 1: Basic Guardrail with Static Credentials + +Deploy an LLM provider with AWS Bedrock Guardrail validation: + +```bash +curl -X POST http://localhost:9090/api/management/v0.9/llm-providers \ + -H "Content-Type: application/yaml" \ + -u "$ADMIN_USERNAME:$ADMIN_PASSWORD" \ + --data-binary @- <<'EOF' +apiVersion: gateway.api-platform.wso2.com/v1alpha1 +kind: LlmProvider +metadata: + name: bedrock-guardrail-provider +spec: + displayName: AWS Bedrock Guardrail Provider + version: v1.0 + template: openai + vhost: openai + upstream: + url: "https://api.openai.com/v1" + auth: + type: api-key + header: Authorization + value: Bearer + accessControl: + mode: deny_all + exceptions: + - path: /chat/completions + methods: [POST] + policies: + - name: aws-bedrock-guardrail + version: v1 + paths: + - path: /chat/completions + methods: [POST] + params: + request: + jsonPath: "$.messages[0].content" + redactPII: false + showAssessment: true + response: + jsonPath: "$.choices[0].message.content" + showAssessment: true +EOF +``` + +**Test the guardrail:** + +**Note**: Ensure that "openai" is mapped to the appropriate IP address (e.g., 127.0.0.1) in your `/etc/hosts` file, or remove the vhost from the LLM provider configuration and use localhost to invoke. + +```bash +# Request with prohibited content (should fail with HTTP 422) +curl -X POST http://openai:8080/chat/completions \ + -H "Content-Type: application/json" \ + -H "Host: openai" \ + -d '{ + "model": "gpt-4", + "messages": [ + { + "role": "user", + "content": "This is prohibited content" + } + ] + }' + +# Request with PII (should mask PII and proceed) +curl -X POST http://openai:8080/chat/completions \ + -H "Content-Type: application/json" \ + -H "Host: openai" \ + -d '{ + "model": "gpt-4", + "messages": [ + { + "role": "user", + "content": "Contact me at user@example.com or call 555-123-4567" + } + ] + }' +``` + +### Example 2: PII Redaction Mode + +Configure to redact PII: + +```yaml +policies: + - name: aws-bedrock-guardrail + version: v1 + paths: + - path: /chat/completions + methods: [POST] + params: + request: + jsonPath: "$.messages[0].content" + redactPII: true # Redact mode + showAssessment: false + response: + jsonPath: "$.choices[0].message.content" +``` + +## Use Cases + +1. **Content Safety**: Enforce enterprise content policies to prevent inappropriate or harmful content from being processed or returned. + +2. **Compliance**: Meet regulatory requirements (HIPAA, GDPR, etc.) by detecting and masking PII in LLM interactions. + +3. **Topic Control**: Restrict LLM usage to approved topics only, preventing misuse or access to sensitive domains. + +4. **Data Privacy**: Mask sensitive information during processing while maintaining the ability to restore it in responses when needed. + +5. **Prohibited Word Filtering**: Block content containing prohibited words, phrases, or patterns defined in your guardrail. + +6. **Multi-tenant Security**: Isolate content policies per tenant or application using different guardrail configurations. + +7. **Audit and Monitoring**: Use detailed assessment information to audit content violations and improve policies. + +## Error Response + +When validation fails, the guardrail returns an HTTP 422 status code with the following structure: + +```json +{ + "type": "AWS_BEDROCK_GUARDRAIL", + "message": { + "action": "GUARDRAIL_INTERVENED", + "interveningGuardrail": "AWS Bedrock Guardrail", + "actionReason": "Violation of AWS Bedrock Guardrail detected.", + "direction": "REQUEST" + } +} +``` + +If `showAssessment` is enabled, additional details are included: + +```json +{ + "type": "AWS_BEDROCK_GUARDRAIL", + "message": { + "action": "GUARDRAIL_INTERVENED", + "interveningGuardrail": "AWS Bedrock Guardrail", + "actionReason": "Violation of AWS Bedrock Guardrail detected.", + "direction": "REQUEST", + "assessments": { + "topicPolicy": { + "topics": ["Topic1", "Topic2"] + }, + "contentPolicy": { + "filters": ["Filter1"] + }, + "sensitiveInformationPolicy": { + "piiEntities": [...], + "regexes": [...] + } + } + } +} +``` + +## Notes + +- The guardrail must be created in AWS Bedrock before use. Use AWS Console, CLI, or SDK to create guardrails with your policies. +- Guardrail version "DRAFT" is useful for testing. Use numbered versions (e.g., "1", "2") for production. +- PII masking with restoration (`redactPII: false`) stores mapping between original and masked values in request metadata, which is used during response processing. +- When using role assumption, ensure the IAM role has `bedrock:ApplyGuardrail` permission. +- The policy uses AWS SDK v2 for authentication and API calls. +- JSONPath extraction failures result in error responses unless `passthroughOnError: true`. +- Content modifications (PII masking) are applied to the payload and forwarded to upstream if no blocking violation occurs. +- The policy validates both request and response phases independently when both are configured. +- Ensure your guardrail is in the specified AWS region; cross-region calls are not supported. diff --git a/en/docs/ai-gateway/1.2.0/llm-proxy/guardrails/azure-content-safety.md b/en/docs/ai-gateway/1.2.0/llm-proxy/guardrails/azure-content-safety.md new file mode 100644 index 000000000..8acdb8de7 --- /dev/null +++ b/en/docs/ai-gateway/1.2.0/llm-proxy/guardrails/azure-content-safety.md @@ -0,0 +1,363 @@ +--- +title: "Azure Content Safety" +description: "Validate LLM request and response content against Azure Content Safety API, detecting hate speech, sexual content, and violence." +canonical_url: https://wso2.com/api-platform/docs/ai-gateway/llm-proxy/guardrails/azure-content-safety/ +md_url: https://wso2.com/api-platform/docs/ai-gateway/llm-proxy/guardrails/azure-content-safety.md +tags: + - ai-gateway + - guardrails + - azure +author: WSO2 API Platform Documentation Team +last_updated: 2026-06-16 +content_type: "reference" +--- + +# Azure Content Safety + +## Overview + +The Azure Content Safety guardrail validates request or response body content against Microsoft Azure Content Safety API for content moderation. It detects and blocks harmful content across four categories: hate speech, sexual content, self-harm, and violence. Each category can be configured with a severity threshold (0-7) or disabled entirely, providing flexible content moderation policies tailored to your application's requirements. + +The policy uses Azure Content Safety's text analysis API to evaluate content and blocks requests or responses that exceed configured severity thresholds. This enables enterprise-grade content filtering for LLM applications integrated with Azure services. + +## Features + +- **Multi-category detection**: Detects hate speech, sexual content, self-harm, and violence +- **Configurable severity thresholds**: Set per-category thresholds (0-7) or disable categories +- **Eight severity levels**: Uses Azure's 8-level severity scale (0=Safe, 7=Most severe) +- **JSONPath support**: Extract and validate specific fields within JSON payloads +- **Separate request/response configuration**: Independent configuration for request and response phases +- **Detailed assessment information**: Optional detailed violation information in error responses +- **Error handling**: Configurable passthrough behavior on API errors +- **Retry logic**: Automatic retry with exponential backoff for transient API failures + +## Configuration + +### Parameters + +#### Request Phase + +| Parameter | Type | Required | Default | Description | +|-----------|------|----------|---------|-------------| +| `jsonPath` | string | No | `""` | JSONPath expression to extract a specific value from JSON payload. If empty, validates the entire payload as a string. | +| `passthroughOnError` | boolean | No | `false` | If `true`, allows requests to proceed if Azure Content Safety API call fails. If `false`, blocks requests on API errors. | +| `showAssessment` | boolean | No | `false` | If `true`, includes detailed assessment information in error responses. | +| `hateCategory` | integer | No | `-1` | Severity threshold for hate category (0-7). `-1` disables this category. Content with severity >= threshold will be blocked. | +| `sexualCategory` | integer | No | `-1` | Severity threshold for sexual category (0-7). `-1` disables this category. Content with severity >= threshold will be blocked. | +| `selfHarmCategory` | integer | No | `-1` | Severity threshold for self-harm category (0-7). `-1` disables this category. Content with severity >= threshold will be blocked. | +| `violenceCategory` | integer | No | `-1` | Severity threshold for violence category (0-7). `-1` disables this category. Content with severity >= threshold will be blocked. | + +#### Response Phase + +| Parameter | Type | Required | Default | Description | +|-----------|------|----------|---------|-------------| +| `jsonPath` | string | No | `""` | JSONPath expression to extract a specific value from JSON payload. If empty, validates the entire payload as a string. | +| `passthroughOnError` | boolean | No | `false` | If `true`, allows requests to proceed if Azure Content Safety API call fails. If `false`, blocks requests on API errors. | +| `showAssessment` | boolean | No | `false` | If `true`, includes detailed assessment information in error responses. | +| `hateCategory` | integer | No | `-1` | Severity threshold for hate category (0-7). `-1` disables this category. Content with severity >= threshold will be blocked. | +| `sexualCategory` | integer | No | `-1` | Severity threshold for sexual category (0-7). `-1` disables this category. Content with severity >= threshold will be blocked. | +| `selfHarmCategory` | integer | No | `-1` | Severity threshold for self-harm category (0-7). `-1` disables this category. Content with severity >= threshold will be blocked. | +| `violenceCategory` | integer | No | `-1` | Severity threshold for violence category (0-7). `-1` disables this category. Content with severity >= threshold will be blocked. | + +### System Parameters (Required) + +These parameters are typically configured at the gateway level and automatically injected, or you can override those values from the params section in the api artifact definition file as well: + +| Parameter | Type | Required | Description | +|-----------|------|----------|-------------| +| `azureContentSafetyEndpoint` | string | Yes | Azure Content Safety API endpoint URL (without trailing slash). Example: `https://your-resource.cognitiveservices.azure.com` | +| `azureContentSafetyKey` | string | Yes | Azure Content Safety API subscription key for authentication. Found in Azure Portal under your Content Safety resource's "Keys and Endpoint" section. | + +### Configuring System Parameters in config.toml + +System parameters can be configured globally in the gateway's `config.toml` file. These values serve as defaults for all Azure Content Safety guardrail policy instances and can be overridden per-policy in the API configuration if needed. + +#### Location in config.toml + +Add the following configuration section to your `config.toml` file: + +```toml +azurecontentsafety_endpoint = "https://your-resource.cognitiveservices.azure.com" +azurecontentsafety_key = "" +``` + +## Severity Levels + +Azure Content Safety uses an 8-level severity scale (0-7): + +- **0**: Safe - No harmful content detected +- **1-2**: Low severity - Mildly concerning content +- **3-4**: Medium severity - Moderately concerning content +- **5-6**: High severity - Highly concerning content +- **7**: Maximum severity - Most severe harmful content + +**Threshold Configuration**: +- Set a threshold value (0-7) to block content at or above that severity level +- Set to `-1` to disable monitoring for that category +- Example: `hateCategory: 3` blocks content with hate severity >= 3 + +## JSONPath Support + +The guardrail supports JSONPath expressions to extract and validate specific fields within JSON payloads. Common examples: + +- `$.message` - Extracts the `message` field from the root object +- `$.data.content` - Extracts nested content from `data.content` +- `$.items[0].text` - Extracts text from the first item in an array +- `$.messages[0].content` - Extracts content from the first message in a messages array +- `$.messages[-1].content` - Extracts content from the last message in a messages array +- `$.choices[0].message.content` - Extracts assistant response content + +If `jsonPath` is empty or not specified, the entire payload is treated as a string and validated. + +## Examples + +### Example 1: Basic Content Moderation + +Deploy an LLM provider with Azure Content Safety validation: + +```bash +curl -X POST http://localhost:9090/api/management/v0.9/llm-providers \ + -H "Content-Type: application/yaml" \ + -u "$ADMIN_USERNAME:$ADMIN_PASSWORD" \ + --data-binary @- <<'EOF' +apiVersion: gateway.api-platform.wso2.com/v1alpha1 +kind: LlmProvider +metadata: + name: azure-safety-provider +spec: + displayName: Azure Content Safety Provider + version: v1.0 + template: openai + vhost: openai + upstream: + url: "https://api.openai.com/v1" + auth: + type: api-key + header: Authorization + value: Bearer + accessControl: + mode: deny_all + exceptions: + - path: /chat/completions + methods: [POST] + policies: + - name: azure-content-safety-content-moderation + version: v1 + paths: + - path: /chat/completions + methods: [POST] + params: + request: + jsonPath: "$.messages[0].content" + hateCategory: 2 + sexualCategory: 2 + selfHarmCategory: 2 + violenceCategory: 2 + showAssessment: true + response: + jsonPath: "$.choices[0].message.content" + hateCategory: 2 + sexualCategory: 2 + selfHarmCategory: 2 + violenceCategory: 2 + showAssessment: true +EOF +``` + +**Test the guardrail:** + +**Note**: Ensure that "openai" is mapped to the appropriate IP address (e.g., 127.0.0.1) in your `/etc/hosts` file, or remove the vhost from the LLM provider configuration and use localhost to invoke. + +```bash +# Request with harmful content (should fail with HTTP 422) +curl -X POST http://openai:8080/chat/completions \ + -H "Content-Type: application/json" \ + -H "Host: openai" \ + -d '{ + "model": "gpt-4", + "messages": [ + { + "role": "user", + "content": "Summarize this article and suggest ways someone could take revenge on Mr. Anonymous Senator violently." + } + ] + }' + +# Valid request (should pass) +curl -X POST http://openai:8080/chat/completions \ + -H "Content-Type: application/json" \ + -H "Host: openai" \ + -d '{ + "model": "gpt-4", + "messages": [ + { + "role": "user", + "content": "Explain quantum computing in simple terms" + } + ] + }' +``` + +### Example 2: Strict Moderation with All Categories + +Configure strict moderation thresholds: + +```yaml +policies: + - name: azure-content-safety-content-moderation + version: v1 + paths: + - path: /chat/completions + methods: [POST] + params: + request: + jsonPath: "$.messages[-1].content" + hateCategory: 1 + sexualCategory: 1 + selfHarmCategory: 1 + violenceCategory: 1 + showAssessment: true + passthroughOnError: false + response: + jsonPath: "$.choices[0].message.content" + hateCategory: 1 + sexualCategory: 1 + selfHarmCategory: 1 + violenceCategory: 1 + showAssessment: true +``` + +### Example 3: Selective Category Monitoring + +Monitor only specific categories: + +```yaml +policies: + - name: azure-content-safety-content-moderation + version: v1 + paths: + - path: /chat/completions + methods: [POST] + params: + request: + jsonPath: "$.messages[0].content" + hateCategory: 3 + sexualCategory: -1 # Disabled + selfHarmCategory: 2 + violenceCategory: -1 # Disabled +``` + +### Example 4: Lenient Moderation + +Allow more content with higher thresholds: + +```yaml +policies: + - name: azure-content-safety-content-moderation + version: v1 + paths: + - path: /chat/completions + methods: [POST] + params: + request: + jsonPath: "$.messages[0].content" + hateCategory: 5 + sexualCategory: 5 + selfHarmCategory: 4 + violenceCategory: 5 + passthroughOnError: true +``` + +## Use Cases + +1. **Content Safety**: Protect users from harmful, offensive, or inappropriate content in LLM interactions. + +2. **Regulatory Compliance**: Meet content moderation requirements for regulated industries or geographies. + +3. **Brand Safety**: Ensure LLM responses align with brand values and don't generate problematic content. + +4. **User Protection**: Prevent exposure to self-harm content, especially important for mental health applications. + +5. **Community Guidelines**: Enforce community standards for user-generated content processed through LLMs. + +6. **Multi-tenant Applications**: Apply different moderation policies per tenant or application context. + +7. **Gradual Rollout**: Start with lenient thresholds and tighten based on actual content patterns. + +8. **Audit and Analytics**: Use detailed assessment information to analyze content patterns and refine policies. + +## Severity Threshold Guidelines + +**Recommended thresholds by use case**: + +- **Strict (Family-friendly applications)**: 1-2 across all categories +- **Moderate (General business applications)**: 3-4 across all categories +- **Lenient (Technical/professional contexts)**: 5-6 for most categories, disable non-applicable ones +- **Educational/Research**: 4-5 with selective category monitoring + +**Category-specific considerations**: + +- **Hate**: Typically set to 2-3 for most applications +- **Sexual**: Set based on application context (1 for family apps, 3-4 for general use) +- **Self-harm**: Often set lower (1-2) due to safety concerns +- **Violence**: Depends on context (1-2 for general use, higher for educational/historical content) + +## Error Response + +When validation fails, the guardrail returns an HTTP 422 status code with the following structure: + +```json +{ + "type": "AZURE_CONTENT_SAFETY_CONTENT_MODERATION", + "message": { + "action": "GUARDRAIL_INTERVENED", + "interveningGuardrail": "azure-content-safety-content-moderation", + "actionReason": "Violation of Azure content safety content moderation detected.", + "direction": "REQUEST" + } +} +``` + +If `showAssessment` is enabled, additional details are included: + +```json +{ + "type": "AZURE_CONTENT_SAFETY_CONTENT_MODERATION", + "message": { + "action": "GUARDRAIL_INTERVENED", + "interveningGuardrail": "azure-content-safety-content-moderation", + "actionReason": "Violation of Azure content safety content moderation detected.", + "direction": "REQUEST", + "assessments": { + "inspectedContent": "The content that was analyzed", + "categories": [ + { + "category": "Hate", + "severity": 4, + "result": "FAIL" + }, + { + "category": "Violence", + "severity": 2, + "result": "FAIL" + } + ] + } + } +} +``` + +## Notes + +- Azure Content Safety API requires an active Azure subscription and Content Safety resource. +- The API endpoint URL must not include a trailing slash (e.g., `https://resource.cognitiveservices.azure.com`). +- API keys are found in Azure Portal under your Content Safety resource's "Keys and Endpoint" section. +- Category thresholds are independent - you can disable any category by setting it to `-1`. +- Only categories with thresholds >= 0 are sent to the Azure API for analysis (performance optimization). +- JSONPath extraction failures result in error responses unless `passthroughOnError: true`. +- The policy validates both request and response phases independently when both are configured. +- Content is sent to Azure Content Safety API for analysis, so ensure compliance with data residency requirements. +- Rate limits may apply based on your Azure Content Safety subscription tier. +- The API uses Azure's 8-severity-level analysis, providing fine-grained control over content moderation. +- For production deployments, monitor API response times and adjust retry/timeout settings if needed. + diff --git a/en/docs/ai-gateway/1.2.0/llm-proxy/guardrails/content-length.md b/en/docs/ai-gateway/1.2.0/llm-proxy/guardrails/content-length.md new file mode 100644 index 000000000..5c9f12489 --- /dev/null +++ b/en/docs/ai-gateway/1.2.0/llm-proxy/guardrails/content-length.md @@ -0,0 +1,207 @@ +--- +title: "Content Length Guardrail" +description: "Enforce minimum and maximum byte length limits on LLM request and response payloads using the Content Length guardrail." +canonical_url: https://wso2.com/api-platform/docs/ai-gateway/llm-proxy/guardrails/content-length/ +md_url: https://wso2.com/api-platform/docs/ai-gateway/llm-proxy/guardrails/content-length.md +tags: + - ai-gateway + - guardrails + - policies +author: WSO2 API Platform Documentation Team +last_updated: 2026-06-16 +content_type: "reference" +--- + +# Content Length Guardrail + +## Overview + +The Content Length Guardrail validates the byte length of request or response body content against configurable minimum and maximum thresholds. This guardrail is essential for controlling payload sizes, preventing resource exhaustion, and ensuring efficient data transfer. + +## Features + +- Validates byte length against minimum and maximum thresholds +- Supports JSONPath extraction to validate specific fields within JSON payloads +- Configurable inverted logic to pass when content length is outside the range +- Separate configuration for request and response phases +- Optional detailed assessment information in error responses + +## Configuration + +### Parameters + +#### Request Phase + +| Parameter | Type | Required | Default | Description | +|-----------|------|----------|---------|-------------| +| `min` | integer | Yes | - | Minimum allowed byte length (inclusive). Must be >= 0. | +| `max` | integer | Yes | - | Maximum allowed byte length (inclusive). Must be >= 1. | +| `jsonPath` | string | No | `""` | JSONPath expression to extract a specific value from JSON payload. If empty, validates the entire payload as a string. | +| `invert` | boolean | No | `false` | If `true`, validation passes when content length is NOT within the min-max range. If `false`, validation passes when content length is within the range. | +| `showAssessment` | boolean | No | `false` | If `true`, includes detailed assessment information in error responses. | + +#### Response Phase + +| Parameter | Type | Required | Default | Description | +|-----------|------|----------|---------|-------------| +| `min` | integer | Yes | - | Minimum allowed byte length (inclusive). Must be >= 0. | +| `max` | integer | Yes | - | Maximum allowed byte length (inclusive). Must be >= 1. | +| `jsonPath` | string | No | `""` | JSONPath expression to extract a specific value from JSON payload. If empty, validates the entire payload as a string. | +| `invert` | boolean | No | `false` | If `true`, validation passes when content length is NOT within the min-max range. If `false`, validation passes when content length is within the range. | +| `showAssessment` | boolean | No | `false` | If `true`, includes detailed assessment information in error responses. | + +## JSONPath Support + +The guardrail supports JSONPath expressions to extract and validate specific fields within JSON payloads. Common examples: + +- `$.message` - Extracts the `message` field from the root object +- `$.data.content` - Extracts nested content from `data.content` +- `$.items[0].text` - Extracts text from the first item in an array +- `$.messages[0].content` - Extracts content from the first message in a messages array + +If `jsonPath` is empty or not specified, the entire payload is treated as a string and validated. + +## Examples + +### Example 1: Basic Content Length Validation + +Deploy an LLM provider that limits request payloads to between 100 bytes and 1MB: + +```bash +curl -X POST http://localhost:9090/api/management/v0.9/llm-providers \ + -H "Content-Type: application/yaml" \ + -u "$ADMIN_USERNAME:$ADMIN_PASSWORD" \ + --data-binary @- <<'EOF' +apiVersion: gateway.api-platform.wso2.com/v1alpha1 +kind: LlmProvider +metadata: + name: content-length-provider +spec: + displayName: Content Length Provider + version: v1.0 + template: openai + vhost: openai + upstream: + url: "https://api.openai.com/v1" + auth: + type: api-key + header: Authorization + value: Bearer + accessControl: + mode: deny_all + exceptions: + - path: /chat/completions + methods: [POST] + - path: /models + methods: [GET] + - path: /models/{modelId} + methods: [GET] + policies: + - name: content-length-guardrail + version: v1 + paths: + - path: /chat/completions + methods: [POST] + params: + request: + min: 100 + max: 1048576 +EOF +``` + +**Test the guardrail:** + +**Note**: Ensure that "openai" is mapped to the appropriate IP address (e.g., 127.0.0.1) in your `/etc/hosts` file, or remove the vhost from the llm provider configuration and use localhost to invoke. + +```bash +# Valid request (should pass) +curl -X POST http://openai:8080/chat/completions \ + -H "Content-Type: application/json" \ + -H "Host: openai" \ + -d '{ + "model": "gpt-4", + "messages": [ + { + "role": "user", + "content": "Please explain artificial intelligence in simple terms for beginners" + } + ] + }' + +# Invalid request - too small (should fail with HTTP 422) +curl -X POST http://openai:8080/chat/completions \ + -H "Content-Type: application/json" \ + -H "Host: openai" \ + -d '{ + "model": "gpt-4", + "messages": [ + { + "role": "user", + "content": "Hi" + } + ] + }' +``` + +### Additional Configuration Options + +You can customize the guardrail behavior by modifying the `policies` section: + +- **Request and Response Validation**: Configure both `request` and `response` parameters to validate byte lengths in both directions. Use `showAssessment: true` to include detailed assessment information in error responses. + +- **Inverted Logic**: Set `invert: true` to allow only content *outside* the specified byte range. This is useful for blocking content that falls within a prohibited size range. + +- **Full Payload Validation**: Omit the `jsonPath` parameter to validate the entire request body without JSONPath extraction. + +- **Field-Specific Validation**: Use `jsonPath` to extract and validate specific fields within JSON payloads (e.g., `"$.messages[0].content"` for message content or `"$.choices[0].message.content"` for response content). + +## Use Cases + +1. **Resource Protection**: Prevent excessively large payloads that could exhaust system resources or cause performance degradation. + +2. **Network Optimization**: Control payload sizes to optimize network transfer times and reduce bandwidth costs. + +3. **Storage Management**: Limit content sizes to manage storage requirements effectively. + +4. **API Rate Limiting**: Enforce size constraints as part of rate limiting strategies. + +5. **Quality Assurance**: Ensure responses meet minimum size requirements for completeness. + +## Error Response + +When validation fails, the guardrail returns an HTTP 422 status code with the following structure: + +```json +{ + "type": "CONTENT_LENGTH_GUARDRAIL", + "message": { + "action": "GUARDRAIL_INTERVENED", + "interveningGuardrail": "content-length-guardrail", + "actionReason": "Violation of applied content length constraints detected.", + "direction": "REQUEST" + } +} +``` + +If `showAssessment` is enabled, additional details are included: + +```json +{ + "type": "CONTENT_LENGTH_GUARDRAIL", + "message": { + "action": "GUARDRAIL_INTERVENED", + "interveningGuardrail": "content-length-guardrail", + "actionReason": "Violation of applied content length constraints detected.", + "assessments": "Violation of content length detected. Expected between 10 and 100 bytes.", + "direction": "REQUEST" + } +} +``` + +## Notes + +- Byte length is calculated on the UTF-8 encoded representation of the content. +- When using JSONPath, if the path does not exist or the extracted value is not a string, validation will fail. +- Inverted logic is useful for blocking content that falls outside acceptable size ranges. +- Consider network and storage constraints when setting maximum values. +- Minimum values help ensure content quality and completeness. diff --git a/en/docs/ai-gateway/1.2.0/llm-proxy/guardrails/json-schema.md b/en/docs/ai-gateway/1.2.0/llm-proxy/guardrails/json-schema.md new file mode 100644 index 000000000..1d920674e --- /dev/null +++ b/en/docs/ai-gateway/1.2.0/llm-proxy/guardrails/json-schema.md @@ -0,0 +1,234 @@ +--- +title: "JSON Schema Guardrail" +description: "Validate LLM request and response body content against a JSON Schema definition to enforce structured data formats." +canonical_url: https://wso2.com/api-platform/docs/ai-gateway/llm-proxy/guardrails/json-schema/ +md_url: https://wso2.com/api-platform/docs/ai-gateway/llm-proxy/guardrails/json-schema.md +tags: + - ai-gateway + - guardrails + - policies +author: WSO2 API Platform Documentation Team +last_updated: 2026-06-16 +content_type: "reference" +--- + +# JSON Schema Guardrail + +## Overview + +The JSON Schema Guardrail validates request or response body content against a JSON Schema definition. This guardrail enables structured data validation, ensuring that JSON payloads conform to expected formats, data types, and constraints. + +## Features + +- Validates content against JSON Schema Draft 7 +- Supports JSONPath extraction to validate specific fields within JSON payloads +- Configurable inverted logic to pass when schema validation fails +- Separate configuration for request and response phases +- Detailed validation error information in error responses + +## Configuration + +### Parameters + +#### Request Phase + +| Parameter | Type | Required | Default | Description | +|-----------|------|----------|---------|-------------| +| `schema` | string | Yes | - | JSON Schema as a string (must be valid JSON). Supports all JSON Schema draft 7 features. | +| `jsonPath` | string | No | `""` | JSONPath expression to extract a specific value from JSON payload for validation. If empty, validates the entire payload against the schema. | +| `invert` | boolean | No | `false` | If `true`, validation passes when schema validation FAILS. If `false`, validation passes when schema validation succeeds. | +| `showAssessment` | boolean | No | `false` | If `true`, includes detailed validation error information in error responses. | + +#### Response Phase + +| Parameter | Type | Required | Default | Description | +|-----------|------|----------|---------|-------------| +| `schema` | string | Yes | - | JSON Schema as a string (must be valid JSON). Supports all JSON Schema draft 7 features. | +| `jsonPath` | string | No | `""` | JSONPath expression to extract a specific value from JSON payload for validation. If empty, validates the entire payload against the schema. | +| `invert` | boolean | No | `false` | If `true`, validation passes when schema validation FAILS. If `false`, validation passes when schema validation succeeds. | +| `showAssessment` | boolean | No | `false` | If `true`, includes detailed validation error information in error responses. | + +## JSONPath Support + +The guardrail supports JSONPath expressions to extract and validate specific fields within JSON payloads. Common examples: + +- `$.data` - Extracts the `data` object for validation +- `$.userInfo` - Extracts user information object +- `$.items[0]` - Extracts the first item in an array +- `$.messages[0]` - Extracts the first message object + +If `jsonPath` is empty or not specified, the entire payload is validated against the schema. + +## JSON Schema Features + +The guardrail supports JSON Schema Draft 7, including: + +- **Types**: `string`, `number`, `integer`, `boolean`, `object`, `array`, `null` +- **Properties**: Define object properties and their schemas +- **Required Fields**: Specify which properties are mandatory +- **Constraints**: `minLength`, `maxLength`, `minimum`, `maximum`, `pattern`, `enum` +- **Nested Structures**: Complex nested objects and arrays +- **Conditional Logic**: `if`, `then`, `else`, `allOf`, `anyOf`, `oneOf`, `not` + +## Examples + +### Example 1: Basic Object Validation + +Deploy an LLM provider that validates that request contains a user object with required fields: + +```bash +curl -X POST http://localhost:9090/api/management/v0.9/llm-providers \ + -H "Content-Type: application/yaml" \ + -u "$ADMIN_USERNAME:$ADMIN_PASSWORD" \ + --data-binary @- <<'EOF' +apiVersion: gateway.api-platform.wso2.com/v1alpha1 +kind: LlmProvider +metadata: + name: json-schema-provider +spec: + displayName: JSON Schema Provider + version: v1.0 + template: openai + vhost: openai + upstream: + url: "https://api.openai.com/v1" + auth: + type: api-key + header: Authorization + value: Bearer + accessControl: + mode: deny_all + exceptions: + - path: /chat/completions + methods: [POST] + - path: /models + methods: [GET] + - path: /models/{modelId} + methods: [GET] + policies: + - name: json-schema-guardrail + version: v1 + paths: + - path: /chat/completions + methods: [POST] + params: + request: + schema: | + { + "type": "object", + "properties": { + "name": {"type": "string", "minLength": 1}, + "email": {"type": "string", "format": "email"}, + "age": {"type": "integer", "minimum": 18} + }, + "required": ["name", "email"] + } +EOF +``` + +**Test the guardrail:** + +**Note**: Ensure that "openai" is mapped to the appropriate IP address (e.g., 127.0.0.1) in your `/etc/hosts` file, or remove the vhost from the llm provider configuration and use localhost to invoke. + +```bash +# Valid request (should pass) +curl -X POST http://openai:8080/chat/completions \ + -H "Content-Type: application/json" \ + -H "Host: openai" \ + -d '{ + "model": "gpt-4", + "messages": [ + { + "role": "user", + "content": "Hello" + } + ], + "name": "John Doe", + "email": "john@example.com", + "age": 25 + }' + +# Invalid request - missing required fields (should fail with HTTP 422) +curl -X POST http://openai:8080/chat/completions \ + -H "Content-Type: application/json" \ + -H "Host: openai" \ + -d '{ + "model": "gpt-4", + "messages": [ + { + "role": "user", + "content": "Hello" + } + ] + }' +``` + +### Additional Configuration Options + +You can customize the guardrail behavior by modifying the `policies` section: + +- **Request and Response Validation**: Configure both `request` and `response` parameters to validate JSON schemas in both directions. Use `showAssessment: true` to include detailed validation error information in error responses. + +- **Inverted Logic**: Set `invert: true` to allow only content that does *not* match the schema. This is useful for blocking requests that match specific schema patterns. + +- **Full Payload Validation**: Omit the `jsonPath` parameter to validate the entire request body against the schema. + +- **Field-Specific Validation**: Use `jsonPath` to extract and validate specific fields within JSON payloads (e.g., `"$.messages[0]"` for message objects or `"$.results"` for response arrays). + +## Use Cases + +1. **API Contract Enforcement**: Ensure requests and responses conform to API specifications. + +2. **Data Quality**: Validate data structure and types before processing. + +3. **Security**: Enforce required fields and prevent injection of unexpected data structures. + +4. **Integration**: Ensure compatibility with downstream systems that expect specific formats. + +5. **Compliance**: Enforce data formats required by regulatory standards. + +## Error Response + +When validation fails, the guardrail returns an HTTP 422 status code with the following structure: + +```json +{ + "type": "JSON_SCHEMA_GUARDRAIL", + "message": { + "action": "GUARDRAIL_INTERVENED", + "interveningGuardrail": "json-schema-guardrail", + "actionReason": "Violation of JSON schema detected.", + "direction": "REQUEST" + } +} +``` + +If `showAssessment` is enabled, detailed validation errors are included: + +```json +{ + "type": "JSON_SCHEMA_GUARDRAIL", + "message": { + "action": "GUARDRAIL_INTERVENED", + "interveningGuardrail": "json-schema-guardrail", + "actionReason": "Violation of JSON schema detected.", + "assessments": [ + { + "description": "String length must be greater than or equal to 5", + "field": "messages.0.content", + "value": "Hi" + } + ], + "direction": "REQUEST" + } +} +``` + +## Notes + +- The schema must be valid JSON. Use proper escaping when embedding in YAML. +- JSON Schema Draft 7 is supported with all standard features. +- When using JSONPath, if the path does not exist or the extracted value is not valid JSON, validation will fail. +- Inverted logic is useful for blocking content that matches specific schema patterns. +- Complex schemas may impact performance; test thoroughly with expected content volumes. +- The guardrail validates the structure and types but does not validate business logic or semantic meaning. diff --git a/en/docs/ai-gateway/1.2.0/llm-proxy/guardrails/pii-masking-regex.md b/en/docs/ai-gateway/1.2.0/llm-proxy/guardrails/pii-masking-regex.md new file mode 100644 index 000000000..2f2b042ac --- /dev/null +++ b/en/docs/ai-gateway/1.2.0/llm-proxy/guardrails/pii-masking-regex.md @@ -0,0 +1,213 @@ +--- +title: "PII Masking Regex Guardrail" +description: "Mask or redact personally identifiable information from LLM request and response bodies using configurable regex patterns." +canonical_url: https://wso2.com/api-platform/docs/ai-gateway/llm-proxy/guardrails/pii-masking-regex/ +md_url: https://wso2.com/api-platform/docs/ai-gateway/llm-proxy/guardrails/pii-masking-regex.md +tags: + - ai-gateway + - guardrails + - security +author: WSO2 API Platform Documentation Team +last_updated: 2026-06-16 +content_type: "reference" +--- + +# PII Masking Regex Guardrail + +## Overview + +The PII Masking Regex Guardrail masks or redacts Personally Identifiable Information (PII) from request and response bodies using configurable regular expression patterns. This guardrail helps protect sensitive user data by replacing PII with placeholders or redaction markers before content is processed or returned. + +## Features + +- Configurable PII entity detection using regular expressions +- Two modes: masking (reversible) and redaction (permanent) +- Automatic PII restoration in responses when using masking mode +- Supports JSONPath extraction to process specific fields within JSON payloads + +## Configuration + +### Parameters + +| Parameter | Type | Required | Default | Description | +|-----------|------|----------|---------|-------------| +| `piiEntities` | array | Yes | - | Array of PII entity configurations. Each entity contains `piiEntity` (name/type) and `piiRegex` (regular expression pattern). | +| `jsonPath` | string | No | `""` | JSONPath expression to extract a specific value from JSON payload. If empty, processes the entire payload as a string. | +| `redactPII` | boolean | No | `false` | If `true`, redacts PII by replacing with "*****" (permanent, cannot be restored). If `false`, masks PII with placeholders that can be restored in responses. | + +### PII Entity Configuration + +Each PII entity in the `piiEntities` array must contain: + +| Field | Type | Required | Description | +|-------|------|----------|-------------| +| `piiEntity` | string | Yes | Name/type of the PII entity (e.g., "EMAIL", "PHONE", "SSN", "CREDIT_CARD"). Must contain only uppercase letters and underscores (matches `^[A-Z_]+$`). | +| `piiRegex` | string | Yes | Regular expression pattern to match the PII entity. Must be a valid Go regexp pattern. | + +## JSONPath Support + +The guardrail supports JSONPath expressions to extract and process specific fields within JSON payloads. Common examples: + +- `$.message` - Extracts the `message` field from the root object +- `$.data.content` - Extracts nested content from `data.content` +- `$.items[0].text` - Extracts text from the first item in an array +- `$.messages[0].content` - Extracts content from the first message in a messages array + +If `jsonPath` is empty or not specified, the entire payload is processed as a string. + +## PII Masking Modes + +### Masking Mode (`redactPII: false`) + +- PII is replaced with placeholders in the format `[ENTITY_TYPE_XXXX]` where XXXX is a 4-digit hexadecimal number (e.g., `[EMAIL_0000]`, `[EMAIL_0001]`, `[PHONE_000a]`) +- Placeholders are automatically restored in responses to their original values +- Original PII values are stored temporarily in request metadata for restoration +- Recommended when you need to preserve data for downstream processing or response generation + +### Redaction Mode (`redactPII: true`) + +- PII is permanently replaced with "*****" +- Cannot be restored in responses +- More secure but loses original data +- Recommended for maximum privacy protection when original values are not needed + +## Examples + +### Example 1: Basic PII Masking + +Deploy an LLM provider that masks email addresses and phone numbers in requests and restores them in responses: + +```bash +curl -X POST http://localhost:9090/api/management/v0.9/llm-providers \ + -H "Content-Type: application/yaml" \ + -u "$ADMIN_USERNAME:$ADMIN_PASSWORD" \ + --data-binary @- <<'EOF' +apiVersion: gateway.api-platform.wso2.com/v1alpha1 +kind: LlmProvider +metadata: + name: pii-masking-provider +spec: + displayName: PII Masking Provider + version: v1.0 + template: openai + vhost: openai + upstream: + url: "https://api.openai.com/v1" + auth: + type: api-key + header: Authorization + value: Bearer + accessControl: + mode: deny_all + exceptions: + - path: /chat/completions + methods: [POST] + - path: /models + methods: [GET] + - path: /models/{modelId} + methods: [GET] + policies: + - name: pii-masking-regex + version: v1 + paths: + - path: /chat/completions + methods: [POST] + params: + piiEntities: + - piiEntity: "EMAIL" + piiRegex: "[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}" + - piiEntity: "PHONE" + piiRegex: "\\+?[1-9]\\d{1,14}" + jsonPath: "$.messages[0].content" + redactPII: true +EOF +``` + +**Test the guardrail:** + +**Note**: Ensure that "openai" is mapped to the appropriate IP address (e.g., 127.0.0.1) in your `/etc/hosts` file, or remove the vhost from the llm provider configuration and use localhost to invoke. + +```bash +# Request with PII (should be masked) +curl -X POST http://openai:8080/chat/completions \ + -H "Content-Type: application/json" \ + -H "Host: openai" \ + -d '{ + "model": "gpt-4", + "messages": [ + { + "role": "user", + "content": "Contact me at john.doe@example.com or call +1234567890" + } + ] + }' +``` + +### Additional Configuration Options + +You can customize the guardrail behavior by modifying the `policies` section: + +- **PII Redaction**: Set `redactPII: true` to permanently replace PII with "*****" (cannot be restored). Set `redactPII: false` to use masking mode with placeholders that can be restored in responses. + +- **Multiple PII Types**: Configure multiple `piiEntities` in the array to detect and mask/redact various PII types (e.g., EMAIL, PHONE, CREDIT_CARD, SSN, IP_ADDRESS, DATE_OF_BIRTH). + +- **Full Payload Processing**: Omit the `jsonPath` parameter to process the entire request body without JSONPath extraction. + +- **Field-Specific Processing**: Use `jsonPath` to extract and process PII from specific fields within JSON payloads (e.g., `"$.messages[0].content"` for message content). + +- **Response Restoration**: When using masking mode (`redactPII: false`), PII is automatically restored in responses. No separate response configuration is needed. If `redactPII: true`, no restoration occurs in the response phase. + +## Use Cases + +1. **Privacy Protection**: Mask or redact PII before sending data to AI services or external systems. + +2. **Compliance**: Meet regulatory requirements (GDPR, CCPA, HIPAA) for PII handling. + +3. **Data Minimization**: Reduce exposure of sensitive data in logs, analytics, or third-party integrations. + +4. **Secure Processing**: Allow AI processing while protecting user privacy through masking. + +5. **Audit Trail**: Maintain masked versions of data for auditing while protecting original values. + +## How It Works + +### Request Phase (Masking) + +1. Extract content using JSONPath (if specified) or use entire payload +2. Apply each PII regex pattern to find matches +3. Replace matches with placeholders (`[ENTITY_TYPE_XXXX]`) or redaction markers (`*****`) +4. Store mapping of placeholders to original values (for masking mode) +5. Forward masked content to upstream service + +### Response Phase (Restoration) + +1. Check if PII was masked in the request phase (metadata contains PII mappings) +2. If `redactPII: false` and mappings exist, replace placeholders with original PII values +3. If `redactPII: true`, no restoration is performed (PII was permanently redacted) +4. Return restored content or original response + + +#### Sample Payload after intervention from Regex PII Masking with redact=true + +``` +{ + "messages": [ + { + "role": "user", + "content": "Prepare an email with my contact information, email: *****, and website: https://example.com." + } + ] +} +``` + +## Notes + +- Regular expressions use Go's regexp package (RE2 syntax). +- PII detection is case-sensitive by default. Use `(?i)` flag for case-insensitive matching. +- The `piiEntity` name must contain only uppercase letters and underscores (e.g., "EMAIL", "PHONE_NUMBER", "SSN"). +- When using masking mode, the placeholder-to-original mapping is stored in request metadata and automatically used for response restoration. +- Multiple PII entities can match the same content; each match is processed according to its entity type. +- Placeholder format is `[ENTITY_TYPE_XXXX]` where XXXX is a 4-digit hexadecimal number (e.g., `[EMAIL_0000]`, `[EMAIL_0001]`, `[PHONE_000a]`). +- When using JSONPath, if the path does not exist or the extracted value is not a string, an error response (HTTP 500) is returned. +- Redaction mode is irreversible; use masking mode if you need to restore PII in responses. +- Complex regex patterns may impact performance; test thoroughly with expected content volumes. diff --git a/en/docs/ai-gateway/1.2.0/llm-proxy/guardrails/regex.md b/en/docs/ai-gateway/1.2.0/llm-proxy/guardrails/regex.md new file mode 100644 index 000000000..ee3dfb424 --- /dev/null +++ b/en/docs/ai-gateway/1.2.0/llm-proxy/guardrails/regex.md @@ -0,0 +1,217 @@ +--- +title: "Regex Guardrail" +description: "Validate LLM request and response content against regular expression patterns to enforce formats or detect prohibited content." +canonical_url: https://wso2.com/api-platform/docs/ai-gateway/llm-proxy/guardrails/regex/ +md_url: https://wso2.com/api-platform/docs/ai-gateway/llm-proxy/guardrails/regex.md +tags: + - ai-gateway + - guardrails + - policies +author: WSO2 API Platform Documentation Team +last_updated: 2026-06-16 +content_type: "reference" +--- + +# Regex Guardrail + +## Overview + +The Regex Guardrail validates request or response body content against regular expression patterns. This guardrail enables pattern-based content validation, allowing you to enforce specific formats, detect prohibited patterns, or ensure content matches expected structures. + +## Features + +- Pattern matching using regular expressions +- Supports JSONPath extraction to validate specific fields within JSON payloads +- Configurable inverted logic to pass when pattern does not match +- Separate configuration for request and response phases +- Optional detailed assessment information in error responses + +## Configuration + +### Parameters + +#### Request Phase + +| Parameter | Type | Required | Default | Description | +|-----------|------|----------|---------|-------------| +| `regex` | string | Yes | - | Regular expression pattern to match against the content. Must be at least 1 character. | +| `jsonPath` | string | No | `""` | JSONPath expression to extract a specific value from JSON payload. If empty, validates the entire payload as a string. | +| `invert` | boolean | No | `false` | If `true`, validation passes when regex does NOT match. If `false`, validation passes when regex matches. | +| `showAssessment` | boolean | No | `false` | If `true`, includes detailed assessment information in error responses. | + +#### Response Phase + +| Parameter | Type | Required | Default | Description | +|-----------|------|----------|---------|-------------| +| `regex` | string | Yes | - | Regular expression pattern to match against the content. Must be at least 1 character. | +| `jsonPath` | string | No | `""` | JSONPath expression to extract a specific value from JSON payload. If empty, validates the entire payload as a string. | +| `invert` | boolean | No | `false` | If `true`, validation passes when regex does NOT match. If `false`, validation passes when regex matches. | +| `showAssessment` | boolean | No | `false` | If `true`, includes detailed assessment information in error responses. | + +## JSONPath Support + +The guardrail supports JSONPath expressions to extract and validate specific fields within JSON payloads. Common examples: + +- `$.message` - Extracts the `message` field from the root object +- `$.data.content` - Extracts nested content from `data.content` +- `$.items[0].text` - Extracts text from the first item in an array +- `$.messages[0].content` - Extracts content from the first message in a messages array + +If `jsonPath` is empty or not specified, the entire payload is treated as a string and validated. + +## Regular Expression Syntax + +The guardrail uses Go's standard regexp package, which supports RE2 syntax. Key features: + +- Case-sensitive matching by default +- Use `(?i)` flag for case-insensitive matching +- Anchors: `^` (start), `$` (end) +- Character classes: `[a-z]`, `[0-9]`, `\d`, `\w`, `\s` +- Quantifiers: `*`, `+`, `?`, `{n}`, `{n,m}` +- Groups and alternation: `(abc|def)`, `(?:non-capturing)` + +## Examples + +### Example 1: Email Validation + +Deploy an LLM provider that protects against sensitive data leaks by blocking any payloads that mention the word "password" (case-insensitive) in either the user’s message or the LLM’s response. This is achieved by using the regex policy to validate both request and response payloads: + +```bash +curl -X POST http://localhost:9090/api/management/v0.9/llm-providers \ + -H "Content-Type: application/yaml" \ + -u "$ADMIN_USERNAME:$ADMIN_PASSWORD" \ + --data-binary @- <<'EOF' +apiVersion: gateway.api-platform.wso2.com/v1alpha1 +kind: LlmProvider +metadata: + name: regex-provider +spec: + displayName: Regex Provider + version: v1.0 + template: openai + vhost: openai + upstream: + url: "https://api.openai.com/v1" + auth: + type: api-key + header: Authorization + value: Bearer + accessControl: + mode: deny_all + exceptions: + - path: /chat/completions + methods: [POST] + - path: /models + methods: [GET] + - path: /models/{modelId} + methods: [GET] + policies: + - name: regex-guardrail + version: v1 + paths: + - path: /chat/completions + methods: [POST] + params: + request: + regex: "(?i).*password.*" + invert: true + jsonPath: "$.messages[0].content" +EOF +``` + +**Test the guardrail:** + +**Note**: Ensure that "openai" is mapped to the appropriate IP address (e.g., 127.0.0.1) in your `/etc/hosts` file, or remove the vhost from the llm provider configuration and use localhost to invoke. + +```bash +# Valid request (should pass) +curl -X POST http://openai:8080/chat/completions \ + -H "Content-Type: application/json" \ + -H "Host: openai" \ + -d '{ + "model": "gpt-4", + "messages": [ + { + "role": "user", + "content": "This is a safe message without sensitive data" + } + ] + }' + +# Invalid request - no email (should fail with HTTP 422) +curl -X POST http://openai:8080/chat/completions \ + -H "Content-Type: application/json" \ + -H "Host: openai" \ + -d '{ + "model": "gpt-4", + "messages": [ + { + "role": "user", + "content": "My password is 1234567" + } + ] + }' +``` + +### Additional Configuration Options + +You can customize the guardrail behavior by modifying the `policies` section: + +- **Request and Response Validation**: Configure both `request` and `response` parameters to validate patterns in both directions. Use `showAssessment: true` to include detailed assessment information in error responses. + +- **Inverted Logic**: Set `invert: true` to allow only content that does *not* match the regex pattern. This is useful for blocking prohibited patterns (e.g., password-related content, admin keywords). + +- **Full Payload Validation**: Omit the `jsonPath` parameter to validate the entire request body without JSONPath extraction. + +- **Field-Specific Validation**: Use `jsonPath` to extract and validate specific fields within JSON payloads (e.g., `"$.messages[0].content"` for message content or `"$.choices[0].message.content"` for response content). + +## Use Cases + +1. **Format Validation**: Ensure user inputs match expected formats (emails, phone numbers, IDs). + +2. **Content Filtering**: Block or allow content based on pattern matching (prohibited words, sensitive patterns). + +3. **Security Enforcement**: Detect and block potentially malicious patterns or injection attempts. + +4. **Data Quality**: Ensure responses follow specific formatting requirements or contain required elements. + +5. **Compliance**: Enforce patterns required by regulatory standards or business rules. + +## Error Response + +When validation fails, the guardrail returns an HTTP 422 status code with the following structure: + +```json +{ + "type": "REGEX_GUARDRAIL", + "message": { + "action": "GUARDRAIL_INTERVENED", + "interveningGuardrail": "regex-guardrail", + "actionReason": "Violation of regular expression detected.", + "direction": "REQUEST" + } +} +``` + +If `showAssessment` is enabled, additional details are included: + +```json +{ + "type": "REGEX_GUARDRAIL", + "message": { + "action": "GUARDRAIL_INTERVENED", + "interveningGuardrail": "regex-guardrail", + "actionReason": "Violation of regular expression detected.", + "assessments": "Violation of regular expression detected. (?i)ignore\\s+all\\s+previous\\s+instructions", + "direction": "REQUEST" + } +} +``` + +## Notes + +- Regular expressions are evaluated using Go's regexp package (RE2 syntax). +- Pattern matching is case-sensitive by default. Use `(?i)` flag for case-insensitive matching. +- When using JSONPath, if the path does not exist or the extracted value is not a string, validation will fail. +- Inverted logic is useful for blocking content that matches prohibited patterns. +- Complex regex patterns may impact performance; test thoroughly with expected content volumes. diff --git a/en/docs/ai-gateway/1.2.0/llm-proxy/guardrails/semantic-prompt-guard.md b/en/docs/ai-gateway/1.2.0/llm-proxy/guardrails/semantic-prompt-guard.md new file mode 100644 index 000000000..07a69ce63 --- /dev/null +++ b/en/docs/ai-gateway/1.2.0/llm-proxy/guardrails/semantic-prompt-guard.md @@ -0,0 +1,410 @@ +--- +title: "Semantic Prompt Guardrail" +description: "Block semantically similar prompts using embedding-based similarity matching against configured allow and deny phrase lists." +canonical_url: https://wso2.com/api-platform/docs/ai-gateway/llm-proxy/guardrails/semantic-prompt-guard/ +md_url: https://wso2.com/api-platform/docs/ai-gateway/llm-proxy/guardrails/semantic-prompt-guard.md +tags: + - ai-gateway + - guardrails + - llm +author: WSO2 API Platform Documentation Team +last_updated: 2026-06-16 +content_type: "reference" +--- + +# Semantic Prompt Guardrail + +## Overview + +The Semantic Prompt Guardrail validates prompts using semantic similarity matching against configured allow and deny phrase lists. Unlike keyword-based filtering, this guardrail understands the meaning of prompts by converting them to vector embeddings and comparing them using cosine similarity. This enables more intelligent content filtering that can catch semantically similar content even when exact keywords differ. + +The policy uses embedding models (OpenAI, Mistral, or Azure OpenAI) to convert prompts and configured phrases into high-dimensional vectors, then performs similarity comparisons. Prompts are blocked if they are too similar to denied phrases or not similar enough to allowed phrases, based on configurable similarity thresholds. + +## Features + +- **Semantic similarity matching**: Uses embeddings to understand meaning, not just keywords +- **Allow/Deny phrase lists**: Configure lists of allowed and denied phrases for flexible filtering +- **Configurable similarity thresholds**: Control matching sensitivity separately for allow and deny lists (0.0 to 1.0) +- **Multiple embedding provider support**: Works with OpenAI, Mistral, and Azure OpenAI embedding services +- **JSONPath extraction**: Extract specific fields from request body for validation +- **Detailed assessment information**: Optional detailed violation information in error responses + +## How It Works + +1. **Text Extraction**: Extracts prompt text from the request body using JSONPath (if configured) or uses the entire request body +2. **Embedding Generation**: Generates a vector embedding from the extracted prompt using the configured embedding provider +3. **Validation Strategy**: The validation logic depends on which lists are configured: + - **Deny list only**: Compares prompt embedding against all denied phrases. If any denied phrase has similarity >= `denySimilarityThreshold`, the request is blocked. Otherwise, it proceeds. + - **Allow list only**: Compares prompt embedding against all allowed phrases. If no allowed phrase has similarity >= `allowSimilarityThreshold`, the request is blocked. Otherwise, it proceeds. + - **Both lists**: First checks the deny list (blocks if similarity >= `denySimilarityThreshold`), then checks the allow list (blocks if similarity < `allowSimilarityThreshold`). Request proceeds only if it passes both checks. +4. **Validation Result**: Request proceeds if validation passes, or is blocked with HTTP 422 if validation fails + +## Configuration + +### Parameters + +| Parameter | Type | Required | Default | Description | +|-----------|------|----------|---------|-------------| +| `jsonPath` | string | No | `""` | JSONPath expression to extract the prompt from JSON payload. If empty, validates the entire payload as a string. Examples: `"$.messages[0].content"`, `"$.prompt"` | +| `allowSimilarityThreshold` | number | No | `0.65` | Minimum similarity threshold (0.0 to 1.0) for a prompt to be considered similar to an allowed phrase. Higher values mean stricter matching. If set, the prompt must match at least one allowed phrase within this threshold. | +| `denySimilarityThreshold` | number | No | `0.65` | Maximum similarity threshold (0.0 to 1.0) for a prompt to be considered similar to a denied phrase. If any denied phrase has similarity >= this threshold, the request is blocked. Higher values mean stricter blocking. | +| `allowedPhrases` | array | No* | `[]` | List of phrases that are considered safe. The prompt must match one of these within `allowSimilarityThreshold` if the threshold is configured. Embeddings are automatically generated during policy initialization. | +| `deniedPhrases` | array | No* | `[]` | List of phrases that should block the prompt when similar within the `denySimilarityThreshold`. Embeddings are automatically generated during policy initialization. | +| `showAssessment` | boolean | No | `false` | If `true`, includes detailed assessment information in error responses. If `false`, returns minimal error information. | + +\* At least one of `allowedPhrases` or `deniedPhrases` must be provided. + +### System Parameters (Required) + +These parameters are typically configured at the gateway level and automatically injected, or you can override those values from the params section in the api artifact definition file as well: + +| Parameter | Type | Required | Description | +|-----------|------|----------|-------------| +| `embeddingProvider` | string | Yes | Embedding provider type. Must be one of: `OPENAI`, `MISTRAL`, `AZURE_OPENAI` | +| `embeddingEndpoint` | string | Yes | Endpoint URL for the embedding service. Examples: OpenAI: `https://api.openai.com/v1/embeddings`, Mistral: `https://api.mistral.ai/v1/embeddings`, Azure OpenAI: Your Azure OpenAI endpoint URL | +| `embeddingModel` | string | Conditional | - | Embedding model name. **Required for OPENAI and MISTRAL**, not required for AZURE_OPENAI (deployment name is in endpoint URL). Examples: OpenAI: `text-embedding-ada-002` or `text-embedding-3-small`, Mistral: `mistral-embed` | +| `apiKey` | string | Yes | API key for the embedding service authentication | + +### Configuring System Parameters in config.toml + +System parameters can be configured globally in the gateway's `config.toml` file. These values serve as defaults for all Semantic Prompt Guard policy instances and can be overridden per-policy in the API configuration if needed. + +#### Location in config.toml + +Add the following configuration section to your `config.toml` file: + +```toml +embedding_provider = "MISTRAL" # Supported: MISTRAL, OPENAI, AZURE_OPENAI +embedding_provider_endpoint = "https://api.mistral.ai/v1/embeddings" +embedding_provider_model = "mistral-embed" +embedding_provider_dimension = 1024 +embedding_provider_api_key = "" +``` + +## Similarity Threshold Guidelines + +The similarity thresholds control how similar prompts must be to trigger allow/deny decisions: + +### Allow Similarity Threshold + +- **0.95-1.0**: Very strict matching. Only near-identical prompts to allowed phrases will pass. Use for exact-match scenarios. +- **0.85-0.94**: Recommended for most use cases. Catches semantically equivalent prompts with some wording variation. +- **0.75-0.84**: More flexible matching. Useful for broader conceptual similarity. +- **0.60-0.74**: Very flexible. May allow loosely related prompts. +- **Below 0.60**: Not recommended. Risk of allowing unrelated prompts. + +**Recommendation**: Start with 0.65 and adjust based on your use case. Monitor false positives/negatives to fine-tune. + +### Deny Similarity Threshold + +- **0.95-1.0**: Very strict blocking. Only near-identical prompts to denied phrases will be blocked. +- **0.85-0.94**: Recommended for most use cases. Catches semantically equivalent prompts with some wording variation. +- **0.75-0.84**: More flexible blocking. Useful for catching variations of prohibited content. +- **0.60-0.74**: Very flexible. May block loosely related prompts. +- **Below 0.60**: Not recommended. Risk of blocking legitimate prompts. + +**Recommendation**: Start with 0.65 and adjust based on your use case. Monitor false positives to fine-tune. + +## JSONPath Support + +The guardrail supports JSONPath expressions to extract specific text from request bodies before validation. This is useful for: +- Extracting message content from chat completion requests +- Focusing on specific prompt fields while ignoring metadata +- Handling structured JSON payloads + +### Common JSONPath Examples + +- `$.messages[0].content` - First message's content in chat completions +- `$.messages[-1].content` - Last message's content +- `$.prompt` - Extract prompt field from completions API +- `$.input` - Extract input field from embeddings API +- `$` - Entire request body (default if jsonPath is not specified) + +## Examples + +### Example 1: Deny List Only - Blocking Prohibited Content + +Deploy an LLM provider that blocks prompts similar to prohibited phrases: + +```bash +curl -X POST http://localhost:9090/api/management/v0.9/llm-providers \ + -H "Content-Type: application/yaml" \ + -u "$ADMIN_USERNAME:$ADMIN_PASSWORD" \ + --data-binary @- <<'EOF' +apiVersion: gateway.api-platform.wso2.com/v1alpha1 +kind: LlmProvider +metadata: + name: semantic-guard-provider +spec: + displayName: Semantic Guard Provider + version: v1.0 + template: openai + vhost: openai + upstream: + url: "https://api.openai.com/v1" + auth: + type: api-key + header: Authorization + value: Bearer + accessControl: + mode: deny_all + exceptions: + - path: /chat/completions + methods: [POST] + policies: + - name: semantic-prompt-guard + version: v1 + paths: + - path: /chat/completions + methods: [POST] + params: + jsonPath: "$.messages[0].content" + denySimilarityThreshold: 0.80 + deniedPhrases: + - "How to hack into a system" + - "Create malicious code" + - "Bypass security measures" + showAssessment: true +EOF +``` + +**Test the guardrail:** + +**Note**: Ensure that "openai" is mapped to the appropriate IP address (e.g., 127.0.0.1) in your `/etc/hosts` file, or remove the vhost from the LLM provider configuration and use localhost to invoke. + +```bash +# Valid request (should pass) +curl -X POST http://openai:8080/chat/completions \ + -H "Content-Type: application/json" \ + -H "Host: openai" \ + -d '{ + "model": "gpt-4", + "messages": [ + { + "role": "user", + "content": "Explain how computer security works" + } + ] + }' + +# Invalid request - similar to denied phrase (should fail with HTTP 422) +curl -X POST http://openai:8080/chat/completions \ + -H "Content-Type: application/json" \ + -H "Host: openai" \ + -d '{ + "model": "gpt-4", + "messages": [ + { + "role": "user", + "content": "How can I break into a computer system?" + } + ] + }' +``` + +### Example 2: Allow List Only - Whitelist Approach + +Deploy an LLM provider that only allows prompts similar to approved phrases: + +```bash +curl -X POST http://localhost:9090/api/management/v0.9/llm-providers \ + -H "Content-Type: application/yaml" \ + -u "$ADMIN_USERNAME:$ADMIN_PASSWORD" \ + --data-binary @- <<'EOF' +apiVersion: gateway.api-platform.wso2.com/v1alpha1 +kind: LlmProvider +metadata: + name: whitelist-provider +spec: + displayName: Whitelist Provider + version: v1.0 + template: openai + vhost: openai + upstream: + url: "https://api.openai.com/v1" + auth: + type: api-key + header: Authorization + value: Bearer + accessControl: + mode: deny_all + exceptions: + - path: /chat/completions + methods: [POST] + policies: + - name: semantic-prompt-guard + version: v1 + paths: + - path: /chat/completions + methods: [POST] + params: + jsonPath: "$.messages[0].content" + allowSimilarityThreshold: 0.75 + allowedPhrases: + - "How can I help you with customer service?" + - "What product information do you need?" + - "Tell me about your order status" + - "I need help with my account" +EOF +``` + +### Example 3: Combined Allow and Deny Lists + +Use both allow and deny lists for comprehensive filtering: + +```yaml +policies: + - name: semantic-prompt-guard + version: v1 + paths: + - path: /chat/completions + methods: [POST] + params: + jsonPath: "$.messages[0].content" + allowSimilarityThreshold: 0.70 + denySimilarityThreshold: 0.75 + allowedPhrases: + - "Customer service inquiry" + - "Product information request" + - "Technical support question" + deniedPhrases: + - "How to hack" + - "Create malware" + - "Bypass authentication" + showAssessment: true +``` + +### Example 4: Azure OpenAI with Custom Timeout + +Configure semantic prompt guardrail with Azure OpenAI and extended timeout: + +```yaml +policies: + - name: semantic-prompt-guard + version: v1 + paths: + - path: /chat/completions + methods: [POST] + params: + jsonPath: "$.messages[-1].content" + denySimilarityThreshold: 0.80 + deniedPhrases: + - "Prohibited content example" + - "Another prohibited phrase" +``` + +## Use Cases + +1. **Content Safety**: Block prompts that are semantically similar to prohibited content, even when exact keywords differ. + +2. **Whitelist Filtering**: Only allow prompts that match approved use cases or topics, ensuring LLM usage stays within defined boundaries. + +3. **Compliance**: Enforce content policies by blocking prompts similar to non-compliant examples. + +4. **Abuse Prevention**: Detect and block variations of known abuse patterns, even when attackers try to evade keyword filters. + +5. **Domain Restriction**: Restrict LLM usage to specific domains by allowing only prompts similar to approved domain-specific phrases. + +6. **Multi-tenant Security**: Apply different allow/deny lists per tenant or application to enforce tenant-specific content policies. + +7. **Prompt Injection Prevention**: Block prompts that are semantically similar to known prompt injection attacks. + +8. **Quality Control**: Ensure prompts match expected patterns for better response quality and consistency. + +## Error Response + +When validation fails, the guardrail returns an HTTP 422 status code with the following structure: + +```json +{ + "type": "SEMANTIC_PROMPT_GUARD", + "message": { + "action": "GUARDRAIL_INTERVENED", + "interveningGuardrail": "semantic-prompt-guard", + "actionReason": "Violation of applied semantic prompt guard constraints detected.", + "direction": "REQUEST" + } +} +``` + +If `showAssessment` is enabled, additional details are included in the `assessments` field: + +```json +{ + "type": "SEMANTIC_PROMPT_GUARD", + "message": { + "action": "GUARDRAIL_INTERVENED", + "interveningGuardrail": "semantic-prompt-guard", + "actionReason": "Violation of applied semantic prompt guard constraints detected.", + "direction": "REQUEST", + "assessments": "prompt is too similar to denied phrase 'How to hack into a system' (similarity=0.8500)" + } +} +``` + +For allow list violations, the assessment message format is: + +```json +{ + "type": "SEMANTIC_PROMPT_GUARD", + "message": { + "action": "GUARDRAIL_INTERVENED", + "interveningGuardrail": "semantic-prompt-guard", + "actionReason": "Violation of applied semantic prompt guard constraints detected.", + "direction": "REQUEST", + "assessments": "prompt is not similar enough to allowed phrases (similarity=0.6000 < threshold=0.6500)" + } +} +``` + +For errors during processing (e.g., JSONPath extraction failures, embedding generation errors), the `actionReason` contains the specific error message: + +```json +{ + "type": "SEMANTIC_PROMPT_GUARD", + "message": { + "action": "GUARDRAIL_INTERVENED", + "interveningGuardrail": "semantic-prompt-guard", + "actionReason": "Error extracting value from JSONPath", + "direction": "REQUEST" + } +} +``` + +## Performance Considerations + +1. **Embedding Generation Latency**: Generating embeddings adds ~100-500ms to request processing. This is a one-time cost per request. + +2. **Batch Processing**: All allow/deny phrase embeddings are generated in a single batch during policy initialization, minimizing initialization overhead. + +3. **Similarity Calculation**: Cosine similarity calculations are fast (typically < 10ms) even with many phrases. + +4. **Embedding Provider Selection**: + - OpenAI: Fast, reliable, good for most use cases + - Mistral: Alternative option with good performance + - Azure OpenAI: Good for Azure-integrated environments + + +## Notes + +- The policy validates prompts in the request phase only (before sending to LLM). Response validation is not supported. + +- Embeddings for allow/deny phrases are generated automatically during policy initialization. Ensure the embedding provider is accessible at initialization time. + +- The policy uses cosine similarity to compare embeddings. This is optimal for semantic similarity matching. + +- At least one of `allowedPhrases` or `deniedPhrases` must be provided. An empty list for both will cause policy initialization to fail. + +- Similarity thresholds are independent for allow and deny lists. You can use different thresholds for each list based on your requirements. + +- JSONPath extraction is optional. If not specified, the entire request body (as string) is used for embedding generation. + +- The `embeddingModel` parameter is required for `OPENAI` and `MISTRAL` providers, but not for `AZURE_OPENAI` (the deployment name is included in the endpoint URL). + +- For Azure OpenAI, the authentication header is automatically set to `api-key`. For other providers, it's set to `Authorization`. + +- The policy processes all phrases in batch during initialization for efficiency. Large phrase lists (100+ phrases) may take a few seconds to initialize. + +- Similarity scores range from 0.0 (no similarity) to 1.0 (identical meaning). Higher thresholds mean stricter matching. + +- For production deployments, monitor false positive/negative rates and adjust similarity thresholds accordingly. diff --git a/en/docs/ai-gateway/1.2.0/llm-proxy/guardrails/sentence-count.md b/en/docs/ai-gateway/1.2.0/llm-proxy/guardrails/sentence-count.md new file mode 100644 index 000000000..d4c8f38b2 --- /dev/null +++ b/en/docs/ai-gateway/1.2.0/llm-proxy/guardrails/sentence-count.md @@ -0,0 +1,217 @@ +--- +title: "Sentence Count Guardrail" +description: "Enforce minimum and maximum sentence count limits on LLM request and response content using the Sentence Count guardrail." +canonical_url: https://wso2.com/api-platform/docs/ai-gateway/llm-proxy/guardrails/sentence-count/ +md_url: https://wso2.com/api-platform/docs/ai-gateway/llm-proxy/guardrails/sentence-count.md +tags: + - ai-gateway + - guardrails + - policies +author: WSO2 API Platform Documentation Team +last_updated: 2026-06-16 +content_type: "reference" +--- + +# Sentence Count Guardrail + +## Overview + +The Sentence Count Guardrail validates the sentence count of request or response body content against configurable minimum and maximum thresholds. This guardrail is useful for ensuring content completeness, controlling response verbosity, and maintaining consistent communication standards. + +## Features + +- Validates sentence count against minimum and maximum thresholds +- Supports JSONPath extraction to validate specific fields within JSON payloads +- Configurable inverted logic to pass when sentence count is outside the range +- Separate configuration for request and response phases +- Optional detailed assessment information in error responses + +## Configuration + +### Parameters + +#### Request Phase + +| Parameter | Type | Required | Default | Description | +|-----------|------|----------|---------|-------------| +| `min` | integer | Yes | - | Minimum allowed sentence count (inclusive). Must be >= 0. | +| `max` | integer | Yes | - | Maximum allowed sentence count (inclusive). Must be >= 1. | +| `jsonPath` | string | No | `""` | JSONPath expression to extract a specific value from JSON payload. If empty, validates the entire payload as a string. | +| `invert` | boolean | No | `false` | If `true`, validation passes when sentence count is NOT within the min-max range. If `false`, validation passes when sentence count is within the range. | +| `showAssessment` | boolean | No | `false` | If `true`, includes detailed assessment information in error responses. | + +#### Response Phase + +| Parameter | Type | Required | Default | Description | +|-----------|------|----------|---------|-------------| +| `min` | integer | Yes | - | Minimum allowed sentence count (inclusive). Must be >= 0. | +| `max` | integer | Yes | - | Maximum allowed sentence count (inclusive). Must be >= 1. | +| `jsonPath` | string | No | `""` | JSONPath expression to extract a specific value from JSON payload. If empty, validates the entire payload as a string. | +| `invert` | boolean | No | `false` | If `true`, validation passes when sentence count is NOT within the min-max range. If `false`, validation passes when sentence count is within the range. | +| `showAssessment` | boolean | No | `false` | If `true`, includes detailed assessment information in error responses. | + +## JSONPath Support + +The guardrail supports JSONPath expressions to extract and validate specific fields within JSON payloads. Common examples: + +- `$.message` - Extracts the `message` field from the root object +- `$.data.content` - Extracts nested content from `data.content` +- `$.items[0].text` - Extracts text from the first item in an array +- `$.messages[0].content` - Extracts content from the first message in a messages array + +If `jsonPath` is empty or not specified, the entire payload is treated as a string and validated. + +## Sentence Detection + +Sentences are detected based on standard sentence-ending punctuation marks: +- Period (.) +- Exclamation mark (!) +- Question mark (?) + +The guardrail counts sequences of characters ending with these punctuation marks as sentences. + +## Examples + +### Example 1: Basic Sentence Count Validation + +Deploy an LLM provider that ensures requests contain between 1 and 10 sentences: + +```bash +curl -X POST http://localhost:9090/api/management/v0.9/llm-providers \ + -H "Content-Type: application/yaml" \ + -u "$ADMIN_USERNAME:$ADMIN_PASSWORD" \ + --data-binary @- <<'EOF' +apiVersion: gateway.api-platform.wso2.com/v1alpha1 +kind: LlmProvider +metadata: + name: sentence-count-provider +spec: + displayName: Sentence Count Provider + version: v1.0 + template: openai + vhost: openai + upstream: + url: "https://api.openai.com/v1" + auth: + type: api-key + header: Authorization + value: Bearer + accessControl: + mode: deny_all + exceptions: + - path: /chat/completions + methods: [POST] + - path: /models + methods: [GET] + - path: /models/{modelId} + methods: [GET] + policies: + - name: sentence-count-guardrail + version: v1 + paths: + - path: /chat/completions + methods: [POST] + params: + request: + min: 2 + max: 10 + jsonPath: "$.messages[0].content" +EOF +``` + +**Test the guardrail:** + +**Note**: Ensure that "openai" is mapped to the appropriate IP address (e.g., 127.0.0.1) in your `/etc/hosts` file, or remove the vhost from the llm provider configuration and use localhost to invoke. + +```bash +# Valid request (should pass) +curl -X POST http://openai:8080/chat/completions \ + -H "Content-Type: application/json" \ + -H "Host: openai" \ + -d '{ + "model": "gpt-4", + "messages": [ + { + "role": "user", + "content": "What is machine learning?. How does it work?. Can you explain it simply?" + } + ] + }' + +# Invalid request - too few sentences (should fail with HTTP 422) +curl -X POST http://openai:8080/chat/completions \ + -H "Content-Type: application/json" \ + -H "Host: openai" \ + -d '{ + "model": "gpt-4", + "messages": [ + { + "role": "user", + "content": "Hi" + } + ] + }' +``` + +### Additional Configuration Options + +You can customize the guardrail behavior by modifying the `policies` section: + +- **Request and Response Validation**: Configure both `request` and `response` parameters to validate sentence counts in both directions. Use `showAssessment: true` to include detailed assessment information in error responses. + +- **Inverted Logic**: Set `invert: true` to allow only content *outside* the specified sentence range. This is useful for blocking content that falls within a prohibited sentence count range. + +- **Full Payload Validation**: Omit the `jsonPath` parameter to validate the entire request body without JSONPath extraction. + +- **Field-Specific Validation**: Use `jsonPath` to extract and validate specific fields within JSON payloads (e.g., `"$.messages[0].content"` for message content or `"$.choices[0].message.content"` for response content). + +## Use Cases + +1. **Content Quality Assurance**: Ensure responses meet minimum sentence requirements for completeness and clarity. + +2. **Response Length Control**: Limit verbosity to maintain concise communication standards. + +3. **Input Validation**: Ensure user prompts contain sufficient context (minimum sentences) without being excessive. + +4. **Consistency Enforcement**: Maintain consistent response formats across different AI interactions. + +5. **Cost Management**: Control response length to manage token usage and associated costs. + +## Error Response + +When validation fails, the guardrail returns an HTTP 422 status code with the following structure: + +```json +{ + "type": "SENTENCE_COUNT_GUARDRAIL", + "message": { + "action": "GUARDRAIL_INTERVENED", + "interveningGuardrail": "sentence-count-guardrail", + "actionReason": "Violation of applied sentence count constraints detected.", + "direction": "REQUEST" + } +} +``` + +If `showAssessment` is enabled, additional details are included: + +```json +{ + "type": "SENTENCE_COUNT_GUARDRAIL", + "message": { + "action": "GUARDRAIL_INTERVENED", + "interveningGuardrail": "sentence-count-guardrail", + "actionReason": "Violation of applied sentence count constraints detected.", + "assessments": "Violation of sentence count detected. Expected between 1 and 3 sentences.", + "direction": "REQUEST" + } +} +``` + +## Notes + +- Sentence counting is performed on the extracted or full content after trimming whitespace. +- Sentences are identified by standard punctuation marks (., !, ?). +- When using JSONPath, if the path does not exist or the extracted value is not a string, validation will fail. +- Inverted logic is useful for blocking content that falls outside acceptable sentence count ranges. +- Consider the nature of your content when setting thresholds, as some content types may naturally have different sentence counts. diff --git a/en/docs/ai-gateway/1.2.0/llm-proxy/guardrails/url.md b/en/docs/ai-gateway/1.2.0/llm-proxy/guardrails/url.md new file mode 100644 index 000000000..c646798a3 --- /dev/null +++ b/en/docs/ai-gateway/1.2.0/llm-proxy/guardrails/url.md @@ -0,0 +1,237 @@ +--- +title: "URL Guardrail" +description: "Validate URLs found in LLM request and response content by checking reachability through DNS resolution or HTTP HEAD requests." +canonical_url: https://wso2.com/api-platform/docs/ai-gateway/llm-proxy/guardrails/url/ +md_url: https://wso2.com/api-platform/docs/ai-gateway/llm-proxy/guardrails/url.md +tags: + - ai-gateway + - guardrails + - policies +author: WSO2 API Platform Documentation Team +last_updated: 2026-06-16 +content_type: "reference" +--- + +# URL Guardrail + +## Overview + +The URL Guardrail validates URLs found in request or response body content by checking their reachability and validity. This guardrail helps prevent broken links, malicious URLs, and ensures that referenced resources are accessible. + +## Features + +- Validates URLs via DNS resolution or HTTP HEAD requests +- Supports JSONPath extraction to validate specific fields within JSON payloads +- Configurable timeout for URL validation +- Separate configuration for request and response phases +- Optional detailed assessment information including invalid URLs in error responses + +## Configuration + +### Parameters + +#### Request Phase + +| Parameter | Type | Required | Default | Description | +|-----------|------|----------|---------|-------------| +| `jsonPath` | string | No | `""` | JSONPath expression to extract a specific value from JSON payload. If empty, validates the entire payload as a string. | +| `onlyDNS` | boolean | No | `false` | If `true`, validates URLs only via DNS resolution (faster, less reliable). If `false`, validates URLs via HTTP HEAD request (slower, more reliable). | +| `timeout` | integer | No | `3000` | Timeout in milliseconds for DNS lookup or HTTP HEAD request. Default is 3000ms (3 seconds). | +| `showAssessment` | boolean | No | `false` | If `true`, includes detailed assessment information including invalid URLs in error responses. | + +#### Response Phase + +| Parameter | Type | Required | Default | Description | +|-----------|------|----------|---------|-------------| +| `jsonPath` | string | No | `""` | JSONPath expression to extract a specific value from JSON payload. If empty, validates the entire payload as a string. | +| `onlyDNS` | boolean | No | `false` | If `true`, validates URLs only via DNS resolution (faster, less reliable). If `false`, validates URLs via HTTP HEAD request (slower, more reliable). | +| `timeout` | integer | No | `3000` | Timeout in milliseconds for DNS lookup or HTTP HEAD request. Default is 3000ms (3 seconds). | +| `showAssessment` | boolean | No | `false` | If `true`, includes detailed assessment information including invalid URLs in error responses. | + +## JSONPath Support + +The guardrail supports JSONPath expressions to extract and validate specific fields within JSON payloads. Common examples: + +- `$.message` - Extracts the `message` field from the root object +- `$.data.content` - Extracts nested content from `data.content` +- `$.items[0].text` - Extracts text from the first item in an array +- `$.messages[0].content` - Extracts content from the first message in a messages array + +If `jsonPath` is empty or not specified, the entire payload is treated as a string and validated. + +## URL Validation Modes + +### DNS-Only Validation (`onlyDNS: true`) + +- Faster validation method +- Only checks if the domain name resolves via DNS +- Does not verify HTTP/HTTPS accessibility +- Less reliable for detecting broken links +- Suitable for quick validation when HTTP checks are not necessary + +### HTTP HEAD Request Validation (`onlyDNS: false`) + +- More thorough validation method +- Performs DNS lookup and HTTP HEAD request +- Verifies that the URL is actually reachable +- More reliable for detecting broken or inaccessible URLs +- Slower due to network request overhead +- Recommended for production use + +## Examples + +### Example 1: Basic URL Validation + +Deploy an LLM provider that validates URLs in request content using HTTP HEAD requests: + +```bash +curl -X POST http://localhost:9090/api/management/v0.9/llm-providers \ + -H "Content-Type: application/yaml" \ + -u "$ADMIN_USERNAME:$ADMIN_PASSWORD" \ + --data-binary @- <<'EOF' +apiVersion: gateway.api-platform.wso2.com/v1alpha1 +kind: LlmProvider +metadata: + name: url-guardrail-provider +spec: + displayName: URL Guardrail Provider + version: v1.0 + template: openai + vhost: openai + upstream: + url: "https://api.openai.com/v1" + auth: + type: api-key + header: Authorization + value: Bearer + accessControl: + mode: deny_all + exceptions: + - path: /chat/completions + methods: [POST] + - path: /models + methods: [GET] + - path: /models/{modelId} + methods: [GET] + policies: + - name: url-guardrail + version: v1 + paths: + - path: /chat/completions + methods: [POST] + params: + request: + jsonPath: "$.messages[0].content" + onlyDNS: false + timeout: 5000 +EOF +``` + +**Test the guardrail:** + +**Note**: Ensure that "openai" is mapped to the appropriate IP address (e.g., 127.0.0.1) in your `/etc/hosts` file, or remove the vhost from the llm provider configuration and use localhost to invoke. + +```bash +# Valid request (should pass) +curl -X POST http://openai:8080/chat/completions \ + -H "Content-Type: application/json" \ + -H "Host: openai" \ + -d '{ + "model": "gpt-4", + "messages": [ + { + "role": "user", + "content": "Visit https://www.example.com for more information" + } + ] + }' + +# Invalid request - invalid URL (should fail with HTTP 422) +curl -X POST http://openai:8080/chat/completions \ + -H "Content-Type: application/json" \ + -H "Host: openai" \ + -d '{ + "model": "gpt-4", + "messages": [ + { + "role": "user", + "content": "Visit https://invalid-url-that-does-not-exist-12345.com" + } + ] + }' +``` + +### Additional Configuration Options + +You can customize the guardrail behavior by modifying the `policies` section: + +- **Request and Response Validation**: Configure both `request` and `response` parameters to validate URLs in both directions. Use `showAssessment: true` to include detailed assessment information including invalid URLs in error responses. + +- **DNS-Only Validation**: Set `onlyDNS: true` for faster validation that only checks DNS resolution. This is less reliable but faster than HTTP HEAD validation. + +- **HTTP HEAD Validation**: Set `onlyDNS: false` (default) for more thorough validation that performs both DNS lookup and HTTP HEAD request to verify URL reachability. + +- **Timeout Configuration**: Adjust the `timeout` parameter (in milliseconds) based on network conditions and acceptable latency. Default is 3000ms (3 seconds). + +- **Full Payload Validation**: Omit the `jsonPath` parameter to validate URLs in the entire request body without JSONPath extraction. + +- **Field-Specific Validation**: Use `jsonPath` to extract and validate URLs from specific fields within JSON payloads (e.g., `"$.messages[0].content"` for message content or `"$.choices[0].message.content"` for response content). + +## Use Cases + +1. **Link Validation**: Ensure all URLs in AI-generated content are valid and accessible. + +2. **Security**: Detect and block potentially malicious or suspicious URLs. + +3. **Quality Assurance**: Prevent broken links from being included in responses. + +4. **Content Moderation**: Validate URLs before allowing them in user-generated content. + +5. **Resource Verification**: Ensure referenced resources are available before processing. + +## Error Response + +When validation fails, the guardrail returns an HTTP 422 status code with the following structure: + +```json +{ + "type": "URL_GUARDRAIL", + "message": { + "action": "GUARDRAIL_INTERVENED", + "interveningGuardrail": "url-guardrail", + "actionReason": "Violation of url validity detected.", + "direction": "REQUEST" + } +} +``` + +If `showAssessment` is enabled, additional details including invalid URLs are included: + +```json +{ + "type": "URL_GUARDRAIL", + "message": { + "action": "GUARDRAIL_INTERVENED", + "interveningGuardrail": "url-guardrail", + "actionReason": "Violation of url validity detected.", + "assessments": { + "invalidUrls": [ + "http://example.com/suspicious-link", + "https://foo.bar.baz" + ], + "message": "One or more URLs in the payload failed validation." + }, + "direction": "REQUEST" + } +} +``` + +## Notes + +- URL validation extracts all URLs from the content using pattern matching. +- DNS-only validation is faster but less reliable than HTTP HEAD validation. +- Timeout values should be set based on network conditions and acceptable latency. +- HTTP HEAD requests may fail for URLs that require specific headers or authentication. +- Some URLs may be temporarily unavailable; consider retry logic for production use. +- When using JSONPath, if the path does not exist or the extracted value is not a string, validation will fail. +- The guardrail validates all URLs found in the content; if any URL is invalid, validation fails. diff --git a/en/docs/ai-gateway/1.2.0/llm-proxy/guardrails/word-count.md b/en/docs/ai-gateway/1.2.0/llm-proxy/guardrails/word-count.md new file mode 100644 index 000000000..73a91d06b --- /dev/null +++ b/en/docs/ai-gateway/1.2.0/llm-proxy/guardrails/word-count.md @@ -0,0 +1,206 @@ +--- +title: "Word Count Guardrail" +description: "Enforce minimum and maximum word count limits on LLM request and response content using the Word Count guardrail." +canonical_url: https://wso2.com/api-platform/docs/ai-gateway/llm-proxy/guardrails/word-count/ +md_url: https://wso2.com/api-platform/docs/ai-gateway/llm-proxy/guardrails/word-count.md +tags: + - ai-gateway + - guardrails + - policies +author: WSO2 API Platform Documentation Team +last_updated: 2026-06-16 +content_type: "reference" +--- + +# Word Count Guardrail + +## Overview + +The Word Count Guardrail validates the word count of request or response body content against configurable minimum and maximum thresholds. This guardrail is useful for enforcing content length policies, ensuring responses meet quality standards, or preventing excessively long inputs that could impact system performance. + +## Features + +- Validates word count against minimum and maximum thresholds +- Supports JSONPath extraction to validate specific fields within JSON payloads +- Configurable inverted logic to pass when word count is outside the range +- Separate configuration for request and response phases +- Optional detailed assessment information in error responses + +## Configuration + +### Parameters + +#### Request Phase + +| Parameter | Type | Required | Default | Description | +|-----------|------|----------|---------|-------------| +| `min` | integer | Yes | - | Minimum allowed word count (inclusive). Must be >= 0. | +| `max` | integer | Yes | - | Maximum allowed word count (inclusive). Must be >= 1. | +| `jsonPath` | string | No | `""` | JSONPath expression to extract a specific value from JSON payload. If empty, validates the entire payload as a string. | +| `invert` | boolean | No | `false` | If `true`, validation passes when word count is NOT within the min-max range. If `false`, validation passes when word count is within the range. | +| `showAssessment` | boolean | No | `false` | If `true`, includes detailed assessment information in error responses. | + +#### Response Phase + +| Parameter | Type | Required | Default | Description | +|-----------|------|----------|---------|-------------| +| `min` | integer | Yes | - | Minimum allowed word count (inclusive). Must be >= 0. | +| `max` | integer | Yes | - | Maximum allowed word count (inclusive). Must be >= 1. | +| `jsonPath` | string | No | `""` | JSONPath expression to extract a specific value from JSON payload. If empty, validates the entire payload as a string. | +| `invert` | boolean | No | `false` | If `true`, validation passes when word count is NOT within the min-max range. If `false`, validation passes when word count is within the range. | +| `showAssessment` | boolean | No | `false` | If `true`, includes detailed assessment information in error responses. | + +## JSONPath Support + +The guardrail supports JSONPath expressions to extract and validate specific fields within JSON payloads. Common examples: + +- `$.message` - Extracts the `message` field from the root object +- `$.data.content` - Extracts nested content from `data.content` +- `$.items[0].text` - Extracts text from the first item in an array +- `$.messages[0].content` - Extracts content from the first message in a messages array + +If `jsonPath` is empty or not specified, the entire payload is treated as a string and validated. + +## Examples + +### Example 1: Basic Word Count Validation + +Deploy an LLM provider that validates request messages contain between 10 and 500 words: + +```bash +curl -X POST http://localhost:9090/api/management/v0.9/llm-providers \ + -H "Content-Type: application/yaml" \ + -u "$ADMIN_USERNAME:$ADMIN_PASSWORD" \ + --data-binary @- <<'EOF' +apiVersion: gateway.api-platform.wso2.com/v1alpha1 +kind: LlmProvider +metadata: + name: word-count-provider +spec: + displayName: Word Count Provider + version: v1.0 + template: openai + vhost: openai + upstream: + url: "https://api.openai.com/v1" + auth: + type: api-key + header: Authorization + value: Bearer + accessControl: + mode: deny_all + exceptions: + - path: /chat/completions + methods: [POST] + - path: /models + methods: [GET] + - path: /models/{modelId} + methods: [GET] + policies: + - name: word-count-guardrail + version: v1 + paths: + - path: /chat/completions + methods: [POST] + params: + request: + min: 5 + max: 500 + jsonPath: "$.messages[0].content" +EOF +``` + +**Test the guardrail:** + +**Note**: Ensure that "openai" is mapped to the appropriate IP address (e.g., 127.0.0.1) in your `/etc/hosts` file, or remove the vhost from the llm provider configuration and use localhost to invoke. + +```bash +# Valid request (should pass) +curl -X POST http://openai:8080/chat/completions \ + -H "Content-Type: application/json" \ + -H "Host: openai" \ + -d '{ + "model": "gpt-4", + "messages": [ + { + "role": "user", + "content": "Please explain artificial intelligence in simple terms for beginners" + } + ] + }' + +# Invalid request - too few words (should fail with HTTP 422) +curl -X POST http://openai:8080/chat/completions \ + -H "Content-Type: application/json" \ + -H "Host: openai" \ + -d '{ + "model": "gpt-4", + "messages": [ + { + "role": "user", + "content": "Hi" + } + ] + }' +``` + +### Additional Configuration Options + +You can customize the guardrail behavior by modifying the `policies` section: + +- **Request and Response Validation**: Configure both `request` and `response` parameters to validate word counts in both directions. Use `showAssessment: true` to include detailed assessment information in error responses. + +- **Inverted Logic**: Set `invert: true` to allow only content *outside* the specified word range. This is useful for blocking content that falls within a prohibited range. + +- **Full Payload Validation**: Omit the `jsonPath` parameter to validate the entire request body without JSONPath extraction. + +- **Field-Specific Validation**: Use `jsonPath` to extract and validate specific fields within JSON payloads (e.g., `"$.messages[0].content"` for message content or `"$.choices[0].message.content"` for response content). + + +## Use Cases + +1. **Input Length Control**: Prevent users from submitting extremely long prompts that could impact system performance or costs. + +2. **Response Quality Assurance**: Ensure AI-generated responses meet minimum length requirements for completeness. + +3. **Cost Management**: Limit response lengths to control token usage and associated costs. + +4. **Content Filtering**: Use inverted logic to block content that falls outside acceptable word count ranges. + +## Error Response + +When validation fails, the guardrail returns an HTTP 422 status code with the following structure: + +```json +{ + "type": "WORD_COUNT_GUARDRAIL", + "message": { + "action": "GUARDRAIL_INTERVENED", + "interveningGuardrail": "word-count-guardrail", + "actionReason": "Violation of applied word count constraints detected.", + "direction": "REQUEST" + } +} +``` + +If `showAssessment` is enabled, additional details are included: + +```json +{ + "type": "WORD_COUNT_GUARDRAIL", + "message": { + "action": "GUARDRAIL_INTERVENED", + "interveningGuardrail": "word-count-guardrail", + "actionReason": "Violation of applied word count constraints detected.", + "assessments": "Violation of word count detected. Expected between 2 and 10 words.", + "direction": "REQUEST" + } +} +``` + +## Notes + +- Word counting is performed on the extracted or full content after trimming whitespace. +- The validation is case-sensitive and counts all words separated by whitespace. +- When using JSONPath, if the path does not exist or the extracted value is not a string, validation will fail. +- Inverted logic is useful for blocking content that falls outside acceptable ranges rather than within them. diff --git a/en/docs/ai-gateway/1.2.0/llm-proxy/llm-templates.md b/en/docs/ai-gateway/1.2.0/llm-proxy/llm-templates.md new file mode 100644 index 000000000..81b224e73 --- /dev/null +++ b/en/docs/ai-gateway/1.2.0/llm-proxy/llm-templates.md @@ -0,0 +1,438 @@ +--- +title: "LLM Provider Templates" +description: "Reference for LLM Provider Templates in API Platform AI Gateway, covering built-in templates for OpenAI, Anthropic, Gemini, and more." +canonical_url: https://wso2.com/api-platform/docs/ai-gateway/llm-proxy/llm-templates/ +md_url: https://wso2.com/api-platform/docs/ai-gateway/llm-proxy/llm-templates.md +tags: + - ai-gateway + - llm + - reference +author: WSO2 API Platform Documentation Team +last_updated: 2026-06-16 +content_type: "reference" +--- + +# LLM Provider Templates + +## Overview + +LLM Provider Templates define the characteristics and behaviors specific to an AI service provider, such as OpenAI, Azure OpenAI, Anthropic, or other LLM platforms. These templates describe how the gateway should interpret and extract usage and operational metadata from LLM provider responses, including: + +- **Token Usage Metrics**: Prompt tokens, completion tokens, total tokens, and remaining tokens +- **Model Information**: Request and response model identifiers +- **Rate Limiting Data**: Remaining token allowances from response + +## Out-of-the-Box Supported Templates + +The API Platform Gateway ships with the following pre-configured LLM provider templates that platform administrators can use immediately without any additional configuration: + +| Template ID | Display Name | Provider | +|-------------|--------------|----------| +| `openai` | OpenAI | OpenAI Provider | +| `azure-openai` | Azure OpenAI | Microsoft Azure OpenAI Provider | +| `anthropic` | Anthropic | Anthropic Claude Provider | +| `gemini` | Gemini | Google Gemini Provider | +| `mistralai` | MistralAI | Mistral AI Provider | +| `awsbedrock` | AWS Bedrock | Amazon Bedrock Provider | +| `azureai-foundry` | Azure AI Foundry | Microsoft Azure AI Foundry Provider | + +These templates are automatically loaded when the gateway starts and are immediately available for use when creating LLM providers. + +## Template Structure + +Each LLM provider template follows a standard YAML structure: + +```yaml +apiVersion: gateway.api-platform.wso2.com/v1alpha1 +kind: LlmProviderTemplate +metadata: + name: +spec: + displayName: + # Provider characteristics + promptTokens: + location: + identifier: + completionTokens: + location: + identifier: + totalTokens: + location: + identifier: + remainingTokens: + location: + identifier: + requestModel: + location: + identifier: + responseModel: + location: + identifier: +``` + +### Metadata Extraction Patterns + +Templates support three types of extraction locations: + +- **`payload`**: Extract from JSON response body using JSONPath expressions (e.g., `$.usage.prompt_tokens`) +- **`header`**: Extract from HTTP response headers using header name (e.g., `x-ratelimit-remaining-tokens`) +- **`pathParam`**: Extract from URL path using regular expressions (e.g., `(?<=models/)[a-zA-Z0-9.\-]+`) + +## Template Details + +### OpenAI + +The OpenAI template extracts metadata from OpenAI API responses. + +```yaml +apiVersion: gateway.api-platform.wso2.com/v1alpha1 +kind: LlmProviderTemplate +metadata: + name: openai +spec: + displayName: OpenAI + promptTokens: + location: payload + identifier: $.usage.prompt_tokens + completionTokens: + location: payload + identifier: $.usage.completion_tokens + totalTokens: + location: payload + identifier: $.usage.total_tokens + remainingTokens: + location: header + identifier: x-ratelimit-remaining-tokens + requestModel: + location: payload + identifier: $.model + responseModel: + location: payload + identifier: $.model +``` + +### Azure OpenAI + +The Azure OpenAI template is compatible with Microsoft's Azure OpenAI Service API. + +```yaml +apiVersion: gateway.api-platform.wso2.com/v1alpha1 +kind: LlmProviderTemplate +metadata: + name: azure-openai +spec: + displayName: Azure OpenAI + promptTokens: + location: payload + identifier: $.usage.input_tokens + completionTokens: + location: payload + identifier: $.usage.output_tokens + totalTokens: + location: payload + identifier: $.usage.total_tokens + remainingTokens: + location: header + identifier: x-ratelimit-remaining-tokens + requestModel: + location: payload + identifier: $.model + responseModel: + location: payload + identifier: $.model +``` + +### Anthropic + +The Anthropic template extracts metadata from Anthropic Claude API responses. + +```yaml +apiVersion: gateway.api-platform.wso2.com/v1alpha1 +kind: LlmProviderTemplate +metadata: + name: anthropic +spec: + displayName: Anthropic + promptTokens: + location: payload + identifier: $.usage.input_tokens + completionTokens: + location: payload + identifier: $.usage.output_tokens + remainingTokens: + location: header + identifier: anthropic-ratelimit-tokens-remaining + requestModel: + location: payload + identifier: $.model + responseModel: + location: payload + identifier: $.model +``` + +### Gemini + +The Gemini template is designed for Google's Gemini API. + +```yaml +apiVersion: gateway.api-platform.wso2.com/v1alpha1 +kind: LlmProviderTemplate +metadata: + name: gemini +spec: + displayName: Gemini + promptTokens: + location: payload + identifier: $.usageMetadata.promptTokenCount + completionTokens: + location: payload + identifier: $.usageMetadata.candidatesTokenCount + totalTokens: + location: payload + identifier: $.usageMetadata.totalTokenCount + remainingTokens: + location: header + identifier: x-ratelimit-remaining-tokens + requestModel: + location: pathParam + identifier: (?<=models/)[a-zA-Z0-9.\-]+ + responseModel: + location: payload + identifier: $.modelVersion +``` + +### MistralAI + +The MistralAI template supports Mistral AI's API. + +```yaml +apiVersion: gateway.api-platform.wso2.com/v1alpha1 +kind: LlmProviderTemplate +metadata: + name: mistralai +spec: + displayName: MistralAI + promptTokens: + location: payload + identifier: $.usage.prompt_tokens + completionTokens: + location: payload + identifier: $.usage.completion_tokens + totalTokens: + location: payload + identifier: $.usage.total_tokens + remainingTokens: + location: header + identifier: x-ratelimit-remaining-tokens + requestModel: + location: payload + identifier: $.model + responseModel: + location: payload + identifier: $.model +``` + +### AWS Bedrock + +The AWS Bedrock template is designed for Amazon Bedrock's unified API. + +```yaml +apiVersion: gateway.api-platform.wso2.com/v1alpha1 +kind: LlmProviderTemplate +metadata: + name: awsbedrock +spec: + displayName: AWS Bedrock + promptTokens: + location: payload + identifier: $.usage.inputTokens + completionTokens: + location: payload + identifier: $.usage.outputTokens + totalTokens: + location: payload + identifier: $.usage.totalTokens + requestModel: + location: pathParam + identifier: (?<=model/)[a-zA-Z0-9.:-]+(?=/) + responseModel: + location: pathParam + identifier: (?<=model/)[a-zA-Z0-9.:-]+(?=/) +``` + +### Azure AI Foundry + +The Azure AI Foundry template supports Microsoft's Azure AI Foundry platform. + +```yaml +apiVersion: gateway.api-platform.wso2.com/v1alpha1 +kind: LlmProviderTemplate +metadata: + name: azureai-foundry +spec: + displayName: Azure AI Foundry + promptTokens: + location: payload + identifier: $.usage.prompt_tokens + completionTokens: + location: payload + identifier: $.usage.completion_tokens + totalTokens: + location: payload + identifier: $.usage.total_tokens + remainingTokens: + location: header + identifier: x-ratelimit-remaining-tokens + requestModel: + location: payload + identifier: $.model + responseModel: + location: payload + identifier: $.model +``` + +## Creating an LLM Provider with a Template + +To create an LLM provider using any of the out-of-the-box templates: + +```bash +curl -X POST http://localhost:9090/api/management/v0.9/llm-providers \ + -H "Content-Type: application/yaml" \ + -u "$ADMIN_USERNAME:$ADMIN_PASSWORD" \ + --data-binary @- <<'EOF' +apiVersion: gateway.api-platform.wso2.com/v1alpha1 +kind: LlmProvider +metadata: + name: +spec: + displayName: + version: v1.0 + template: + upstream: + url: https://api.openai.com/v1 + auth: + type: api-key + header: + value: + accessControl: + mode: deny_all + exceptions: + - path: /chat/completions + methods: [POST] + - path: /models + methods: [GET] + - path: /models/{modelId} + methods: [GET] +EOF +``` + +Replace the placeholders: +- ``: Unique identifier for your provider (e.g., `my-openai-provider`) +- ``: Human-readable name (e.g., `My OpenAI Provider`) +- ``: One of the supported template IDs (`openai`, `azure-openai`, `anthropic`, `gemini`, `mistralai`, `awsbedrock`, `azureai-foundry`) +- ``: Authentication header name (e.g., `Authorization` for most providers) +- ``: Your API key with appropriate prefix (e.g., `Bearer sk-...` for OpenAI) + +The gateway automatically uses the template's metadata extraction patterns to: +- Extract token usage information from responses +- Track model usage for analytics +- Enable token-based rate limiting policies +- Provide consistent monitoring across different LLM providers + +## Managing Templates + +### Listing Available Templates + +To list all available LLM provider templates: + +```bash +curl -X GET http://localhost:9090/api/management/v0.9/llm-provider-templates \ + -u "$ADMIN_USERNAME:$ADMIN_PASSWORD" +``` + +### Retrieving a Specific Template + +To retrieve details of a specific template: + +```bash +curl -X GET http://localhost:9090/api/management/v0.9/llm-provider-templates/openai \ + -u "$ADMIN_USERNAME:$ADMIN_PASSWORD" +``` + +### Creating Custom Templates + +Platform administrators can create custom templates for LLM providers not covered by the out-of-the-box templates: + +```bash +curl -X POST http://localhost:9090/api/management/v0.9/llm-provider-templates \ + -H "Content-Type: application/yaml" \ + -u "$ADMIN_USERNAME:$ADMIN_PASSWORD" \ + --data-binary @- <<'EOF' +apiVersion: gateway.api-platform.wso2.com/v1alpha1 +kind: LlmProviderTemplate +metadata: + name: custom-provider +spec: + displayName: Custom Provider + totalTokens: + location: payload + identifier: $.tokens.total +EOF +``` + +### Updating Templates + +To update an existing custom template: + +```bash +curl -X PUT http://localhost:9090/api/management/v0.9/llm-provider-templates/custom-provider \ + -H "Content-Type: application/yaml" \ + -u "$ADMIN_USERNAME:$ADMIN_PASSWORD" \ + --data-binary @- <<'EOF' +apiVersion: gateway.api-platform.wso2.com/v1alpha1 +kind: LlmProviderTemplate +metadata: + name: custom-provider +spec: + displayName: Custom Provider Updated + promptTokens: + location: payload + identifier: $.usage.input_tokens + # ... other fields +EOF +``` + +### Deleting Custom Templates + +To delete a custom template: + +```bash +curl -X DELETE http://localhost:9090/api/management/v0.9/llm-provider-templates/custom-provider \ + -u "$ADMIN_USERNAME:$ADMIN_PASSWORD" +``` + +**Note**: Out-of-the-box templates cannot be deleted or modified. Only custom templates created by platform administrators can be updated or deleted. + +## Template Field Reference + +| Field | Type | Required | Description | +|-------|------|----------|-------------| +| `apiVersion` | string | Yes | API version, must be `gateway.api-platform.wso2.com/v1alpha1` | +| `kind` | string | Yes | Resource kind, must be `LlmProviderTemplate` | +| `metadata.name` | string | Yes | Unique identifier for the template (used as template ID) | +| `spec.displayName` | string | Yes | Human-readable name for the template | +| `spec.promptTokens` | object | No | Configuration for extracting prompt/input token count | +| `spec.completionTokens` | object | No | Configuration for extracting completion/output token count | +| `spec.totalTokens` | object | No | Configuration for extracting total token count | +| `spec.remainingTokens` | object | No | Configuration for extracting remaining token allowance | +| `spec.requestModel` | object | No | Configuration for extracting request model identifier | +| `spec.responseModel` | object | No | Configuration for extracting response model identifier | + +### Extraction Configuration Object + +Each extraction configuration object has the following structure: + +| Field | Type | Values | Description | +|-------|------|--------|-------------| +| `location` | string | `payload`, `header`, `pathParam` | Where to extract the value from | +| `identifier` | string | - | JSONPath (for payload), header name (for header), or regex pattern (for pathParam) | diff --git a/en/docs/ai-gateway/1.2.0/llm-proxy/load-balancing/model-round-robin.md b/en/docs/ai-gateway/1.2.0/llm-proxy/load-balancing/model-round-robin.md new file mode 100644 index 000000000..a3d55ed6d --- /dev/null +++ b/en/docs/ai-gateway/1.2.0/llm-proxy/load-balancing/model-round-robin.md @@ -0,0 +1,243 @@ +--- +title: "Model Round Robin" +description: "Distribute LLM requests evenly across multiple AI models in a cyclic round-robin pattern with automatic failure suspension." +canonical_url: https://wso2.com/api-platform/docs/ai-gateway/llm-proxy/load-balancing/model-round-robin/ +md_url: https://wso2.com/api-platform/docs/ai-gateway/llm-proxy/load-balancing/model-round-robin.md +tags: + - ai-gateway + - llm + - load-balancing +author: WSO2 API Platform Documentation Team +last_updated: 2026-07-30 +content_type: "reference" +--- + +# Model Round Robin + +## Overview + +The Model Round Robin policy implements round-robin load balancing for AI models. It distributes requests evenly across multiple configured AI models in a cyclic manner, ensuring equal request allocation over time and preventing overloading of any single model. This policy is useful for distributing load across multiple models, improving availability, and managing resource utilization. + +## Features + +- Even distribution of requests across multiple models in a cyclic pattern +- Automatic model suspension on failures (5xx or 429 responses) +- Configurable suspension duration for failed models +- Multi-provider routing by assigning a provider to each model +- Support for extracting model identifier from payload, headers, query parameters, or path parameters +- Dynamic model selection based on availability + +## Configuration + +### Parameters + +| Parameter | Type | Required | Default | Description | +|-----------|------|----------|---------|-------------| +| `models` | array | Yes | - | List of models for round-robin distribution. Each entry must have a `model` name and can optionally select a `provider`. | +| `suspendDuration` | integer | No | `30` | Suspend duration in seconds for failed models. If set to 0, failed model knowledge is not persisted. Must be >= 0. | + +### Model Configuration + +Each model in the `models` array is an object with the following properties: + +| Property | Type | Required | Description | +|----------|------|----------|-------------| +| `model` | string | Yes | The AI model name to use for load balancing. | +| `provider` | string | No | Effective provider name to route the selected model to. Omit this field to use the proxy's primary provider. When an additional provider has an `as` alias, use the alias instead of its provider ID. | + +### LLM provider template + +The policy requires `requestModel` configuration from the LLM provider template to extract the model identifier from the request. This configuration is mandatory and must be provided by the LLM provider template. + +| Parameter | Type | Required | Description | +|-----------|------|----------|-------------| +| `requestModel.location` | string | Yes | Location of the model identifier: `payload`, `header`, `queryParam`, or `pathParam` | +| `requestModel.identifier` | string | Yes | JSONPath (for payload), header name (for header), query param name (for queryParam), or regex pattern (for pathParam) to extract model | + +## How It Works + +1. **Model Selection**: On each request, the policy selects the next available model in the configured list using a round-robin algorithm. +2. **Model Extraction**: The policy extracts the original model from the request (if configured) and stores it for reference. +3. **Model Modification**: The policy modifies the request to use the selected model based on the `requestModel` configuration. +4. **Failure Handling**: If a model returns a 5xx or 429 response, and `suspendDuration` is configured, the model is suspended for the specified duration. +5. **Availability Check**: Suspended models are skipped during selection until their suspension period expires. + +## Examples + +### Multi-provider round robin + +Configure the policy on an LLM proxy to distribute requests across models from its primary and additional providers: + +```yaml +operationPolicies: + - name: model-round-robin + version: v1 + paths: + - path: /chat/completions + methods: [POST] + params: + models: + - model: gpt-4o + - model: claude-sonnet-4-5-20250929 + provider: anthropic-provider + - model: anthropic.claude-3-5-sonnet-20240620-v1:0 + provider: bedrock-provider + suspendDuration: 60 +``` + +The entry without `provider` uses the proxy's primary provider. The other entries route to providers declared under `spec.additionalProviders`. See [Multi-Provider Routing for LLM Proxies](../multi-provider-routing.md) for provider, authentication, transformer, and alias configuration. + +### Example 1: Basic Round Robin with Payload-based Model + +Deploy an LLM provider with round-robin load balancing across multiple models: + +```bash +curl -X POST http://localhost:9090/api/management/v1/llm-providers \ + -H "Content-Type: application/yaml" \ + -u "$ADMIN_USERNAME:$ADMIN_PASSWORD" \ + --data-binary @- <<'EOF' +apiVersion: gateway.api-platform.wso2.com/v1 +kind: LlmProvider +metadata: + name: round-robin-provider +spec: + displayName: Round Robin Provider + version: v1.0 + template: openai + context: /providers/round-robin + upstream: + url: "https://api.openai.com/v1" + auth: + type: api-key + header: Authorization + value: Bearer + accessControl: + mode: deny_all + exceptions: + - path: /chat/completions + methods: [POST] + operationPolicies: + - name: model-round-robin + version: v1 + paths: + - path: /chat/completions + methods: [POST] + params: + models: + - model: gpt-4 + - model: gpt-3.5-turbo + - model: gpt-4-turbo + suspendDuration: 60 +EOF +``` + +**Test the round-robin distribution:** + +```bash +# First request - will use gpt-4 +curl -k -X POST https://localhost:8443/providers/round-robin/chat/completions \ + -H "Content-Type: application/json" \ + -d '{ + "model": "gpt-4", + "messages": [ + { + "role": "user", + "content": "Hello" + } + ] + }' + +# Second request - will use gpt-3.5-turbo +curl -k -X POST https://localhost:8443/providers/round-robin/chat/completions \ + -H "Content-Type: application/json" \ + -d '{ + "model": "gpt-4", + "messages": [ + { + "role": "user", + "content": "Hello" + } + ] + }' + +# Third request - will use gpt-4-turbo +curl -k -X POST https://localhost:8443/providers/round-robin/chat/completions \ + -H "Content-Type: application/json" \ + -d '{ + "model": "gpt-4", + "messages": [ + { + "role": "user", + "content": "Hello" + } + ] + }' +``` + +## Model Suspension + +When a model returns a 5xx or 429 response, the policy can automatically suspend that model for a configurable duration: + +- **Suspension Duration**: Configured via the `suspendDuration` parameter (in seconds) +- **Automatic Recovery**: Suspended models are automatically re-enabled after the suspension period expires +- **Availability Check**: Suspended models are skipped during round-robin selection until they recover + +### Suspension Behavior + +- Suspension is tracked per model across all requests +- If all models are suspended, the policy returns HTTP 503 with error: "All models are currently unavailable" +- Suspension period starts from the time of failure + +## Use Cases + +1. **Load Distribution**: Distribute requests evenly across multiple models to prevent overloading any single model. + +2. **High Availability**: Automatically route requests to available models when some models are experiencing issues. + +3. **Cost Optimization**: Distribute requests across different model tiers (e.g., expensive and cheaper models) to balance cost and performance. + +4. **A/B Testing**: Test different models with equal traffic distribution to compare performance and quality. + +5. **Multi-Provider Support**: Distribute requests across models from different providers while maintaining equal distribution. + +## Request Model Locations + +The policy supports extracting the model identifier from different locations in the request: + +### Payload (JSONPath) + +Extract model from JSON payload using JSONPath: + +- **Location**: `payload` +- **Identifier**: JSONPath expression (e.g., `$.model`, `$.messages[0].model`) + +### Header + +Extract model from HTTP header: + +- **Location**: `header` +- **Identifier**: Header name (e.g., `X-Model-Name`, `X-LLM-Model`) + +### Query Parameter + +Extract model from URL query parameter: + +- **Location**: `queryParam` +- **Identifier**: Query parameter name (e.g., `model`, `llm_model`) + +### Path Parameter + +Extract model from URL path using regex: + +- **Location**: `pathParam` +- **Identifier**: Regex pattern to match model in path (e.g., `models/([a-zA-Z0-9.\-]+)`) + +**Note**: For path parameters, the regex pattern should include a capturing group to extract the model name. The policy uses the first capturing group as the model identifier. + +## Notes + +- The round-robin index is maintained per policy instance and increments for each request. +- Model selection is deterministic and follows a strict cyclic pattern. +- The original model from the request is stored in metadata but is replaced with the selected model for routing. +- If `suspendDuration` is 0, failed models are not suspended and will continue to be selected in the round-robin cycle. +- The `requestModel` configuration is required and must be provided by the LLM provider template. diff --git a/en/docs/ai-gateway/1.2.0/llm-proxy/load-balancing/model-weighted-round-robin.md b/en/docs/ai-gateway/1.2.0/llm-proxy/load-balancing/model-weighted-round-robin.md new file mode 100644 index 000000000..39b6f391c --- /dev/null +++ b/en/docs/ai-gateway/1.2.0/llm-proxy/load-balancing/model-weighted-round-robin.md @@ -0,0 +1,262 @@ +--- +title: "Model Weighted Round Robin" +description: "Distribute LLM requests across AI models by assigned weights, giving higher-capacity or lower-cost models proportionally more traffic." +canonical_url: https://wso2.com/api-platform/docs/ai-gateway/llm-proxy/load-balancing/model-weighted-round-robin/ +md_url: https://wso2.com/api-platform/docs/ai-gateway/llm-proxy/load-balancing/model-weighted-round-robin.md +tags: + - ai-gateway + - llm + - load-balancing +author: WSO2 API Platform Documentation Team +last_updated: 2026-07-30 +content_type: "reference" +--- + +# Model Weighted Round Robin + +## Overview + +The Model Weighted Round Robin policy implements weighted round-robin load balancing for AI models. It distributes requests based on predefined weight values assigned to each model, enabling probabilistic control over request distribution and giving higher priority to models with greater processing power or availability. This policy is useful for distributing load proportionally across models based on their capacity, cost, or performance characteristics. + +## Features + +- Weighted distribution of requests across multiple models based on assigned weights +- Proportional request allocation (models with higher weights receive more requests) +- Automatic model suspension on failures (5xx or 429 responses) +- Configurable suspension duration for failed models +- Multi-provider routing by assigning a provider to each weighted model +- Support for extracting model identifier from payload, headers, query parameters, or path parameters +- Dynamic model selection based on availability and weights + +## Configuration + +### Parameters + +| Parameter | Type | Required | Default | Description | +|-----------|------|----------|---------|-------------| +| `models` | array | Yes | - | List of models with weights for weighted round-robin distribution. Each entry must have a `model` name and `weight`, and can optionally select a `provider`. | +| `suspendDuration` | integer | No | `30` | Suspend duration in seconds for failed models. If set to 0, failed model knowledge is not persisted. Must be >= 0. | + +### Model Configuration + +Each model in the `models` array is an object with the following properties: + +| Property | Type | Required | Description | +|----------|------|----------|-------------| +| `model` | string | Yes | The AI model name to use for load balancing. | +| `weight` | integer | Yes | The weight assigned to this model for distribution. Higher weights mean more requests will be routed to this model. Weight is relative to total weight of all models. Must be at least 1. | +| `provider` | string | No | Effective provider name to route the selected model to. Omit this field to use the proxy's primary provider. When an additional provider has an `as` alias, use the alias instead of its provider ID. | + +### LLM provider template + +The policy requires `requestModel` configuration from the LLM provider template to extract the model identifier from the request. This configuration is mandatory and must be provided by the LLM provider template. + +| Parameter | Type | Required | Description | +|-----------|------|----------|-------------| +| `requestModel.location` | string | Yes | Location of the model identifier: `payload`, `header`, `queryParam`, or `pathParam` | +| `requestModel.identifier` | string | Yes | JSONPath (for payload), header name (for header), query param name (for queryParam), or regex pattern (for pathParam) to extract model | + +## How It Works + +1. **Weight Calculation**: During policy initialization, the policy calculates the total weight of all configured models and builds a weighted sequence where each model appears a number of times proportional to its weight. This sequence is built once and reused for all requests. +2. **Model Selection**: On each request, the policy selects the next available model from the pre-computed weighted sequence using a round-robin algorithm. +3. **Model Extraction**: The policy extracts the original model from the request using the `requestModel` configuration and stores it for reference. +4. **Model Modification**: The policy modifies the request to use the selected model based on the `requestModel` configuration. +5. **Failure Handling**: If a model returns a 5xx or 429 response, and `suspendDuration` is configured, the model is suspended for the specified duration. +6. **Availability Check**: Suspended models are skipped during selection until their suspension period expires. + +### Weight Distribution Example + +If you configure three models with weights: +- Model A: weight 3 +- Model B: weight 2 +- Model C: weight 1 + +The weighted sequence would be: `[A, A, A, B, B, C]`, meaning: +- Model A receives 50% of requests (3 out of 6) +- Model B receives 33.3% of requests (2 out of 6) +- Model C receives 16.7% of requests (1 out of 6) + +## Examples + +### Multi-provider weighted round robin + +Configure the policy on an LLM proxy to distribute traffic by weight across models from its primary and additional providers: + +```yaml +operationPolicies: + - name: model-weighted-round-robin + version: v1 + paths: + - path: /chat/completions + methods: [POST] + params: + models: + - model: gpt-4o + weight: 5 + - model: claude-sonnet-4-5-20250929 + provider: anthropic-provider + weight: 3 + - model: anthropic.claude-3-5-sonnet-20240620-v1:0 + provider: bedrock-provider + weight: 2 + suspendDuration: 60 +``` + +The entry without `provider` uses the proxy's primary provider. The other entries route to providers declared under `spec.additionalProviders`. See [Multi-Provider Routing for LLM Proxies](../multi-provider-routing.md) for provider, authentication, transformer, and alias configuration. + +### Example 1: Basic Weighted Round Robin with Payload-based Model + +Deploy an LLM provider with weighted round-robin load balancing: + +```bash +curl -X POST http://localhost:9090/api/management/v1/llm-providers \ + -H "Content-Type: application/yaml" \ + -u "$ADMIN_USERNAME:$ADMIN_PASSWORD" \ + --data-binary @- <<'EOF' +apiVersion: gateway.api-platform.wso2.com/v1 +kind: LlmProvider +metadata: + name: weighted-round-robin-provider +spec: + displayName: Weighted Round Robin Provider + version: v1.0 + template: openai + context: /providers/weighted-round-robin + upstream: + url: "https://api.openai.com/v1" + auth: + type: api-key + header: Authorization + value: Bearer + accessControl: + mode: deny_all + exceptions: + - path: /chat/completions + methods: [POST] + operationPolicies: + - name: model-weighted-round-robin + version: v1 + paths: + - path: /chat/completions + methods: [POST] + params: + models: + - model: gpt-4 + weight: 3 + - model: gpt-3.5-turbo + weight: 2 + - model: gpt-4-turbo + weight: 1 + suspendDuration: 60 +EOF +``` + +**Test the weighted round-robin distribution:** + +```bash +# Requests will be distributed: 50% gpt-4, 33.3% gpt-3.5-turbo, 16.7% gpt-4-turbo +curl -k -X POST https://localhost:8443/providers/weighted-round-robin/chat/completions \ + -H "Content-Type: application/json" \ + -d '{ + "model": "gpt-4", + "messages": [ + { + "role": "user", + "content": "Hello" + } + ] + }' +``` + +## Model Suspension + +When a model returns a 5xx or 429 response, the policy can automatically suspend that model for a configurable duration: + +- **Suspension Duration**: Configured via the `suspendDuration` parameter (in seconds) +- **Automatic Recovery**: Suspended models are automatically re-enabled after the suspension period expires +- **Availability Check**: Suspended models are skipped during weighted round-robin selection until they recover +- **Weight Preservation**: When a model is suspended, the remaining models continue to be selected based on their relative weights + +### Suspension Behavior + +- If all models are suspended, the policy returns HTTP 503 with error: "All models are currently unavailable" +- Suspension period starts from the time of failure +- When a model is suspended, the weighted sequence is dynamically adjusted to exclude that model + +## Use Cases + +1. **Capacity-Based Distribution**: Distribute requests based on model capacity, giving higher weights to models that can handle more load. + +2. **Cost Optimization**: Route more requests to cheaper models while maintaining some traffic to premium models for quality assurance. + +3. **Performance Tiers**: Prioritize high-performance models for critical requests while using standard models for regular traffic. + +4. **Gradual Migration**: Gradually shift traffic from old models to new models by adjusting weights over time. + +5. **Multi-Provider Balancing**: Distribute requests across models from different providers with different weights based on SLA or cost agreements. + +6. **A/B Testing with Bias**: Test different models with weighted traffic distribution to compare performance while maintaining a bias toward preferred models. + +## Request Model Locations + +The policy supports extracting the model identifier from different locations in the request: + +### Payload (JSONPath) + +Extract model from JSON payload using JSONPath: + +- **Location**: `payload` +- **Identifier**: JSONPath expression (e.g., `$.model`, `$.messages[0].model`) + +### Header + +Extract model from HTTP header: + +- **Location**: `header` +- **Identifier**: Header name (e.g., `X-Model-Name`, `X-LLM-Model`) + +### Query Parameter + +Extract model from URL query parameter: + +- **Location**: `queryParam` +- **Identifier**: Query parameter name (e.g., `model`, `llm_model`) + +### Path Parameter + +Extract model from URL path using regex: + +- **Location**: `pathParam` +- **Identifier**: Regex pattern to match model in path (e.g., `models/([a-zA-Z0-9.\-]+)`) + +**Note**: For path parameters, the regex pattern should include a capturing group to extract the model name. The policy uses the first capturing group as the model identifier. + +## Weight Calculation + +The policy builds a weighted sequence by repeating each model a number of times equal to its weight: + +- **Total Weight**: Sum of all model weights +- **Sequence Length**: Equal to the total weight +- **Distribution**: Each model appears in the sequence `weight` times +- **Proportional Selection**: Over time, each model receives requests proportional to `model_weight / total_weight` + +### Example Weight Distribution + +For models with weights [5, 3, 2]: +- Total weight: 10 +- Sequence: [Model1, Model1, Model1, Model1, Model1, Model2, Model2, Model2, Model3, Model3] +- Model1: 50% of requests +- Model2: 30% of requests +- Model3: 20% of requests + +## Notes + +- The weighted sequence is pre-computed once during policy initialization and reused for all requests. It is not rebuilt on each request. +- The round-robin index is maintained per policy instance and increments for each request. +- Model selection follows the weighted sequence in a deterministic cyclic pattern. +- The original model from the request is stored in metadata but is replaced with the selected model for routing. +- If `suspendDuration` is 0, failed models are not suspended and will continue to be selected in the weighted round-robin cycle. +- Higher weights result in more frequent selection but do not guarantee exact proportional distribution in small request volumes. +- The weighted sequence ensures long-term proportional distribution, but short-term distribution may vary due to suspension and availability. +- The `requestModel` configuration is required and must be provided by the LLM provider template. There is no default behavior. diff --git a/en/docs/ai-gateway/1.2.0/llm-proxy/multi-provider-routing.md b/en/docs/ai-gateway/1.2.0/llm-proxy/multi-provider-routing.md new file mode 100644 index 000000000..78c0d19ec --- /dev/null +++ b/en/docs/ai-gateway/1.2.0/llm-proxy/multi-provider-routing.md @@ -0,0 +1,554 @@ +--- +title: "Multi-Provider Routing for LLM Proxies" +description: "Route OpenAI-compatible LLM proxy requests to multiple providers using header-based selection and provider-specific transformers." +canonical_url: https://wso2.com/api-platform/docs/ai-gateway/llm-proxy/multi-provider-routing/ +md_url: https://wso2.com/api-platform/docs/ai-gateway/llm-proxy/multi-provider-routing.md +tags: + - ai-gateway + - llm + - routing +author: WSO2 API Platform Documentation Team +last_updated: 2026-07-30 +content_type: "guide" +--- + +# Multi-Provider Routing for LLM Proxies + +## Overview + +Multi-provider routing lets one large language model (LLM) proxy expose a single OpenAI-compatible endpoint while routing each request to a selected LLM provider. Applications continue to use the same endpoint and OpenAI-compatible request and response format, even when the upstream provider changes. + +For example, an application can send all requests to `/openai-multi/chat/completions` and select OpenAI or Anthropic with the `x-provider` request header. + +This is useful when you want to: + +- Switch providers without changing application code or endpoint URLs +- Compare provider responses using the same OpenAI-compatible request +- Keep vendor credentials in the gateway instead of distributing them to applications +- Apply proxy-level authentication, rate limits, and guardrails consistently across providers +- Introduce provider fallback or selection logic through a routing policy + +## How It Works + +A multi-provider LLM proxy has: + +- One primary provider in `spec.provider` +- One or more selectable providers in `spec.additionalProviders` +- An LLM Header Router policy (`llm-header-router`) that selects a provider from a request header +- An inline transformer for each additional provider that does not use the OpenAI wire format + +The request flow is: + +```text +OpenAI-compatible client request + | + | x-provider: anthropic + v + Multi-provider LLM proxy + | + | LLM Header Router selects anthropic-provider + | openai-to-anthropic transforms the request + | provider loopback authentication is added + v + Anthropic LLM provider + | + | vendor authentication is added + v + Anthropic API + | + | response is transformed to OpenAI format + v +OpenAI-compatible client response +``` + +The router writes the selected provider name to request metadata. The gateway conditionally applies only the authentication and transformer associated with that provider. When the selection header is missing, empty, or does not match a configured mapping, the router uses `defaultProvider` when configured; otherwise, the proxy's primary provider is used. + +## Before You Begin + +Make sure that: + +- The AI Gateway is running and the management API is available at `http://localhost:9090/api/management/v1`. +- You are using an AI Gateway version that supports multi-provider routing and includes the required router and transformer policies. +- You have credentials for each external LLM provider. +- `curl` and `jq` are installed if you want to follow the command-line examples. + +This guide configures OpenAI as the primary provider and Anthropic as an additional provider. The same configuration model can be extended to Azure OpenAI, Mistral, Gemini, AWS Bedrock, and other providers supported by your AI Gateway version. + +## Understand the Authentication Layers + +Multi-provider routing can involve three different kinds of credentials: + +| Credential | Used by | Purpose | +|------------|---------|---------| +| Vendor credential | LLM provider to external vendor | Authenticates the gateway to OpenAI, Anthropic, or another external service | +| Provider loopback key | LLM proxy to LLM provider | Authenticates the proxy when it routes internally to a protected provider | +| Proxy consumer key | Application to LLM proxy | Authenticates the application invoking the public proxy endpoint | + +Do not use a vendor API key as a loopback or consumer key. Do not commit any of these credentials to source control. + +## Step 1: Deploy the LLM Providers + +Each provider must exist before a proxy can reference it. + +### Deploy the OpenAI provider + +Replace `` with an OpenAI API key. + +```bash +curl -X POST http://localhost:9090/api/management/v1/llm-providers \ + -u admin:admin \ + -H "Content-Type: application/yaml" \ + --data-binary @- <<'EOF' +apiVersion: gateway.api-platform.wso2.com/v1 +kind: LlmProvider +metadata: + name: openai-provider +spec: + displayName: OpenAI Provider + version: v1.0 + template: openai + context: /providers/openai + upstream: + url: https://api.openai.com/v1 + auth: + type: api-key + header: Authorization + value: Bearer + accessControl: + mode: deny_all + exceptions: + - path: /chat/completions + methods: [POST] + operationPolicies: + - name: api-key-auth + version: v1 + paths: + - path: /chat/completions + methods: [POST] + params: + key: X-API-Key + in: header +EOF +``` + +### Deploy the Anthropic provider + +Replace `` with an Anthropic API key. + +```bash +curl -X POST http://localhost:9090/api/management/v1/llm-providers \ + -u admin:admin \ + -H "Content-Type: application/yaml" \ + --data-binary @- <<'EOF' +apiVersion: gateway.api-platform.wso2.com/v1 +kind: LlmProvider +metadata: + name: anthropic-provider +spec: + displayName: Anthropic Provider + version: v1.0 + template: anthropic + context: /providers/anthropic + upstream: + url: https://api.anthropic.com + auth: + type: api-key + header: x-api-key + value: + accessControl: + mode: deny_all + exceptions: + - path: /v1/messages + methods: [POST] + operationPolicies: + - name: api-key-auth + version: v1 + paths: + - path: /v1/messages + methods: [POST] + params: + key: X-API-Key + in: header +EOF +``` + +The vendor credentials under `spec.upstream.auth` are added only when the provider calls its external service. + +## Step 2: Create Provider Loopback Keys + +Because both providers in this example use the `api-key-auth` policy, create an API key for each provider. The proxy uses these keys when routing to the providers through the gateway's internal loopback route. + +```bash +OPENAI_LOOPBACK_KEY=$(curl -s -X POST \ + http://localhost:9090/api/management/v1/llm-providers/openai-provider/api-keys \ + -u admin:admin \ + -H "Content-Type: application/json" \ + -d '{"name":"openai-proxy-loopback"}' \ + | jq -r '.apiKey.apiKey') + +ANTHROPIC_LOOPBACK_KEY=$(curl -s -X POST \ + http://localhost:9090/api/management/v1/llm-providers/anthropic-provider/api-keys \ + -u admin:admin \ + -H "Content-Type: application/json" \ + -d '{"name":"anthropic-proxy-loopback"}' \ + | jq -r '.apiKey.apiKey') +``` + +Verify that both commands returned a value: + +```bash +test -n "$OPENAI_LOOPBACK_KEY" && test "$OPENAI_LOOPBACK_KEY" != "null" +test -n "$ANTHROPIC_LOOPBACK_KEY" && test "$ANTHROPIC_LOOPBACK_KEY" != "null" +``` + +API key values are returned only when they are created or regenerated. Store them securely. + +## Step 3: Deploy the Multi-Provider LLM Proxy + +The following proxy exposes one `/chat/completions` operation. OpenAI is the primary and default provider. Anthropic is an additional selectable provider with an inline request and response transformer. + +```bash +curl -X POST http://localhost:9090/api/management/v1/llm-proxies \ + -u admin:admin \ + -H "Content-Type: application/yaml" \ + --data-binary @- < + transformer: + type: openai-to-azure-openai + version: v1 + params: + model: gpt-4o + apiVersion: "2024-02-15-preview" +``` + +### Mistral + +```yaml +- id: mistral-provider + auth: + type: api-key + header: X-API-Key + value: + transformer: + type: openai-to-mistral + version: v1 + params: + model: mistral-large-latest +``` + +### Gemini + +```yaml +- id: gemini-provider + auth: + type: api-key + header: X-API-Key + value: + transformer: + type: openai-to-gemini + version: v1 + params: + model: gemini-2.5-flash + apiVersion: v1beta +``` + +### AWS Bedrock + +```yaml +- id: aws-bedrock-provider + auth: + type: api-key + header: X-API-Key + value: + transformer: + type: openai-to-bedrock-transformer + version: v1 + params: + model: anthropic.claude-3-5-sonnet-20240620-v1:0 +``` + +For example, the matching router entries are: + +```yaml +mappings: + - headerValue: azure-openai + provider: azure-openai-provider + - headerValue: mistral + provider: mistral-provider + - headerValue: gemini + provider: gemini-provider + - headerValue: aws-bedrock + provider: aws-bedrock-provider +``` + +## Use Provider Aliases + +Use `as` when the logical upstream name used by routing policies should differ from the deployed provider ID: + +```yaml +additionalProviders: + - id: anthropic-provider + as: anthropic-upstream + auth: + type: api-key + header: X-API-Key + value: + transformer: + type: openai-to-anthropic + version: v1 + params: + model: claude-sonnet-4-5-20250929 +``` + +When an alias is present, router mappings must select the alias, not the provider ID: + +```yaml +mappings: + - headerValue: anthropic + provider: anthropic-upstream +``` + +The alias must: + +- Contain only letters, numbers, hyphens, or underscores +- Be between 1 and 100 characters +- Be unique within the proxy +- Not match the primary provider ID or another additional provider's effective name + +## Configuration Reference + +### `additionalProviders` + +| Field | Required | Description | +|-------|----------|-------------| +| `id` | Yes | ID of an already deployed `LlmProvider` | +| `as` | No | Logical upstream name used by routing policies; defaults to `id` | +| `auth` | No | API key authentication used by the proxy when calling the provider's internal route | +| `transformer` | No | Request and response transformer applied only when this provider is selected | + +### `transformer` + +| Field | Required | Description | +|-------|----------|-------------| +| `type` | Yes | Installed transformer policy name, such as `openai-to-anthropic` | +| `version` | Yes | Major policy version, such as `v1` | +| `params` | No | Transformer-specific parameters, such as `model` or `apiVersion` | + +### LLM Header Router parameters + +Use `llm-header-router` as the policy name in the configuration. + +| Parameter | Required | Default | Description | +|-----------|----------|---------|-------------| +| `headerName` | No | `x-provider` | Request header used for selection | +| `defaultProvider` | No | Primary provider | Effective provider name selected when no mapping matches. When omitted, the proxy's primary provider is used. | +| `mappings` | Yes | None | Header value to effective provider name mappings; the first match wins | + +## Validation and Troubleshooting + +### The additional provider is not found + +Deploy every provider before deploying the proxy. Each `additionalProviders[].id` must match the `metadata.name` of an existing `LlmProvider`. + +### The proxy reports a duplicate upstream name + +Every effective provider name must be unique. The effective name is `as` when it is configured; otherwise, it is `id`. It must not collide with the primary provider ID. + +### The transformer is rejected during deployment + +Make sure that: + +- `transformer.type` names a transformer supported by your AI Gateway version. +- `transformer.version` uses a major-only version such as `v1`. +- All parameters required by that transformer are present. + +The gateway resolves the major version to an installed full policy version and rejects invalid transformer configuration during deployment. + +### The request always reaches the default provider + +Check that: + +- The routing policy is attached to the same path and method being invoked. +- The request uses the header configured by `headerName`. +- The header value matches a `mappings[].headerValue`. +- The mapping's `provider` matches the additional provider's `as` value when an alias is configured; otherwise, it matches `id`. + +An unknown header value intentionally falls back to `defaultProvider`. + +### The provider returns `401 Unauthorized` + +Confirm which authentication layer rejected the request: + +- A rejection at the proxy usually means the proxy consumer key is missing or invalid. +- A rejection on the provider's loopback route usually means `provider.auth` or `additionalProviders[].auth` contains an invalid provider API key. +- A rejection from the external vendor usually means `LlmProvider.spec.upstream.auth` contains an invalid vendor credential or uses the wrong header format. + +### The configured transformer is not supported + +The AI Gateway distribution includes the router and transformer policies supported by that version. Use a supported `transformer.type` and major version, or upgrade the AI Gateway to a version that includes the required transformer. + +## Security Recommendations + +- Store vendor credentials and loopback keys in a secret manager or Kubernetes `Secret` instead of committing plain-text values. +- Protect the proxy with an authentication policy so applications cannot invoke it anonymously. +- Expose only required provider operations through `accessControl`. +- Apply rate limiting and guardrails at the provider or proxy level according to your governance requirements. +- Use explicit router mappings. Do not accept a client-provided value as an unrestricted upstream name. + +## Complete Example + +For a larger configuration containing OpenAI, Anthropic, Azure OpenAI, Mistral, Gemini, and AWS Bedrock, see [`gateway/examples/openai-multi-provider-proxy.yaml`](https://github.com/wso2/api-platform/blob/main/gateway/examples/openai-multi-provider-proxy.yaml). + +For automatic traffic distribution across models and providers, see: + +- [Model Round Robin](load-balancing/model-round-robin.md) +- [Model Weighted Round Robin](load-balancing/model-weighted-round-robin.md) + +AWS Bedrock usage can also be evaluated by the [LLM Cost policy](../../../next/ai-workspace/policies/overview.md#llm-cost). diff --git a/en/docs/ai-gateway/1.2.0/llm-proxy/prompt-management/prompt-decorator.md b/en/docs/ai-gateway/1.2.0/llm-proxy/prompt-management/prompt-decorator.md new file mode 100644 index 000000000..b483a6307 --- /dev/null +++ b/en/docs/ai-gateway/1.2.0/llm-proxy/prompt-management/prompt-decorator.md @@ -0,0 +1,325 @@ +--- +title: "Prompt Decorator" +description: "Prepend or append custom content to LLM prompts, injecting system messages or standard instructions before requests reach the model." +canonical_url: https://wso2.com/api-platform/docs/ai-gateway/llm-proxy/prompt-management/prompt-decorator/ +md_url: https://wso2.com/api-platform/docs/ai-gateway/llm-proxy/prompt-management/prompt-decorator.md +tags: + - ai-gateway + - llm + - prompt-management +author: WSO2 API Platform Documentation Team +last_updated: 2026-06-16 +content_type: "reference" +--- + +# Prompt Decorator + +## Overview + +The Prompt Decorator policy dynamically modifies prompts by prepending or appending custom content to specific fields in JSON payloads. This policy supports two decoration modes: **text prompt decoration** (for string content fields) and **chat prompt decoration** (for message arrays). It's useful for adding consistent instructions, system messages, or standardized prefixes/suffixes to prompts before they're sent to AI services. + +## Features + +- Two decoration modes: text decoration (string fields) and chat decoration (message arrays) +- Configurable prepend or append behavior +- JSONPath support for targeting specific fields in JSON payloads +- Flexible decoration format: simple strings or structured message objects +- Processes request body only (response phase not supported) + +## Configuration + +### Parameters + +| Parameter | Type | Required | Default | Description | +|-----------|------|----------|---------|-------------| +| `promptDecoratorConfig` | string | Yes | - | JSON string containing decoration configuration. For text decoration: `{"decoration": "string"}`. For chat decoration: `{"decoration": [{"role": "system", "content": "..."}]}` | +| `jsonPath` | string | Yes | - | JSONPath expression to locate the field to decorate. Use `$.messages[0].content` for text decoration, or `$.messages` for chat decoration. | +| `append` | boolean | No | `false` | If `true`, decoration is appended to the content. If `false`, decoration is prepended (default). | + +## Decoration Modes + +### Mode 1: Text Prompt Decoration + +Text decoration is used when the JSONPath targets a string field (e.g., `$.messages[0].content`). The decoration can be: +- A simple string that gets prepended or appended to the content +- An array of decoration objects (their content fields are concatenated with newlines) + +**Configuration Example:** +```json +{ + "decoration": "Summarize the following content in a concise, neutral, and professional tone. Structure the summary using bullet points if appropriate.\n\n" +} +``` + +**Behavior:** +- Decoration string is prepended or appended to the target content field +- A space is automatically added between the decoration and original content + +### Mode 2: Chat Prompt Decoration + +Chat decoration is used when the JSONPath targets an array field (e.g., `$.messages`). The decoration must be an array of message objects: + +**Configuration Example:** +```json +{ + "decoration": [ + { + "role": "system", + "content": "You are a helpful hotel booking receptionist for the imaginary hotel 'Azure Horizon Resort'. Your job is to collect all the necessary booking details from guests." + } + ] +} +``` + +**Behavior:** +- Decoration messages are prepended or appended to the messages array +- Each decoration object must have `role` and `content` fields +- Multiple decoration messages can be added + +## JSONPath Support + +The decorator supports JSONPath expressions to target specific fields. Common examples: + +- `$.messages[0].content` - First message's content field (text decoration) +- `$.messages[-1].content` - Last message's content field (text decoration) +- `$.messages` - Entire messages array (chat decoration) +- `$.data.text` - Nested text field (text decoration) + +**Array Index Syntax:** +- Use `[0]` for first element, `[1]` for second, etc. +- Use `[-1]` for last element, `[-2]` for second-to-last, etc. + +## Examples + +### Example 1: Text Prompt Decoration - Summarization Directive + +Add a summarization instruction to user prompts: + +```bash +curl -X POST http://localhost:9090/api/management/v0.9/llm-providers \ + -H "Content-Type: application/yaml" \ + -u "$ADMIN_USERNAME:$ADMIN_PASSWORD" \ + --data-binary @- <<'EOF' +apiVersion: gateway.api-platform.wso2.com/v1alpha1 +kind: LlmProvider +metadata: + name: summarization-provider +spec: + displayName: Summarization Provider + version: v1.0 + template: openai + vhost: openai + upstream: + url: "https://api.openai.com/v1" + auth: + type: api-key + header: Authorization + value: Bearer + accessControl: + mode: deny_all + exceptions: + - path: /chat/completions + methods: [POST] + policies: + - name: prompt-decorator + version: v1 + paths: + - path: /chat/completions + methods: [POST] + params: + promptDecoratorConfig: '{"decoration": "Summarize the following content in a concise, neutral, and professional tone. Structure the summary using bullet points if appropriate.\\n\\n"}' + jsonPath: "$.messages[0].content" + append: false +EOF +``` + +**Test the decorator:** + +**Note**: Ensure that "openai" is mapped to the appropriate IP address (e.g., 127.0.0.1) in your `/etc/hosts` file, or remove the vhost from the LLM provider configuration and use localhost to invoke. + +```bash +# Original request +curl -X POST http://openai:8080/chat/completions \ + -H "Content-Type: application/json" \ + -H "Host: openai" \ + -d '{ + "model": "gpt-4", + "messages": [ + { + "role": "user", + "content": "Large text block to summarize here..." + } + ] + }' + +# After decoration, the request sent to OpenAI will be: +# { +# "model": "gpt-4", +# "messages": [ +# { +# "role": "user", +# "content": "Summarize the following content in a concise, neutral, and professional tone. Structure the summary using bullet points if appropriate.\n\n Large text block to summarize here..." +# } +# ] +# } +``` + +### Example 2: Chat Prompt Decoration - System Persona + +Add a system message to define AI behavior: + +```bash +curl -X POST http://localhost:9090/api/management/v0.9/llm-providers \ + -H "Content-Type: application/yaml" \ + -u "$ADMIN_USERNAME:$ADMIN_PASSWORD" \ + --data-binary @- <<'EOF' +apiVersion: gateway.api-platform.wso2.com/v1alpha1 +kind: LlmProvider +metadata: + name: hotel-booking-provider +spec: + displayName: Hotel Booking Provider + version: v1.0 + template: openai + vhost: openai + upstream: + url: "https://api.openai.com/v1" + auth: + type: api-key + header: Authorization + value: Bearer + accessControl: + mode: deny_all + exceptions: + - path: /chat/completions + methods: [POST] + policies: + - name: prompt-decorator + version: v1 + paths: + - path: /chat/completions + methods: [POST] + params: + promptDecoratorConfig: '{"decoration": [{"role": "system", "content": "You are a helpful hotel booking receptionist for Azure Horizon Resort. Collect booking details: name, NIC, check-in time, staying duration (nights), and room type (single, double, suite). Ask one detail at a time in a friendly tone."}]}' + jsonPath: "$.messages" + append: false +EOF +``` + +**Test the decorator:** + +```bash +# Original request +curl -X POST http://openai:8080/chat/completions \ + -H "Content-Type: application/json" \ + -H "Host: openai" \ + -d '{ + "model": "gpt-4", + "messages": [ + { + "role": "user", + "content": "Hi, I would like to book a room." + } + ] + }' + +# After decoration, the request sent to OpenAI will be: +# { +# "model": "gpt-4", +# "messages": [ +# { +# "role": "system", +# "content": "You are a helpful hotel booking receptionist for Azure Horizon Resort. Collect booking details: name, NIC, check-in time, staying duration (nights), and room type (single, double, suite). Ask one detail at a time in a friendly tone." +# }, +# { +# "role": "user", +# "content": "Hi, I would like to book a room." +# } +# ] +# } +``` + +### Example 3: Append Mode - Adding Suffix Instructions + +Append instructions to the end of user messages: + +```yaml +policies: + - name: prompt-decorator + version: v1 + paths: + - path: /chat/completions + methods: [POST] + params: + promptDecoratorConfig: '{"decoration": "\\n\\nPlease respond in JSON format."}' + jsonPath: "$.messages[-1].content" + append: true +``` + +## Use Cases + +1. **Consistent Instructions**: Prepend standardized instructions or guidelines to all prompts to ensure consistent AI behavior. + +2. **System Personas**: Inject system messages to define AI personality, role, or behavior before user interactions. + +3. **Quality Enhancement**: Add formatting instructions (e.g., "respond in bullet points", "use professional tone") to improve response quality. + +4. **Context Addition**: Prepend contextual information or background details to enrich prompts. + +5. **Multi-turn Conversations**: Add system messages at the beginning of chat conversations to set conversation rules. + +6. **Compliance**: Append compliance-related instructions or disclaimers to prompts. + +7. **Output Formatting**: Add instructions for specific output formats (JSON, markdown, structured text) to prompts. + +## Configuration Reference + +### Text Decoration Configuration + +```json +{ + "decoration": "Your decoration string here" +} +``` + +- Simple string that will be prepended or appended to the target content +- A space is automatically added between decoration and original content + +### Chat Decoration Configuration + +```json +{ + "decoration": [ + { + "role": "system", + "content": "Your system message content" + } + ] +} +``` + +- Array of message objects +- Each object must have `role` (e.g., "system", "user", "assistant") and `content` fields +- Messages are prepended or appended to the messages array in the order specified + +## Error Response + +When the policy encounters an error (e.g., invalid JSONPath, missing fields), it returns an HTTP 500 status code with the following structure: + +```json +{ + "type": "PROMPT_DECORATOR_ERROR", + "message": "Error description here" +} +``` + +## Notes + +- The policy only processes request bodies. +- For text decoration, a space is automatically added between the decoration and original content. +- JSONPath expressions must correctly identify the target field. Invalid paths will result in errors. +- When decorating message arrays, ensure the target field is actually an array of message objects. +- The `append: false` (default) means decoration is prepended. Set `append: true` to append decoration. +- Decoration objects in chat mode must have both `role` and `content` fields; both are required. +- Negative array indices (e.g., `[-1]` for last element) are supported in JSONPath expressions. +- When using text decoration with an array of decoration objects, their content fields are concatenated with newlines (`\n`). diff --git a/en/docs/ai-gateway/1.2.0/llm-proxy/prompt-management/prompt-template.md b/en/docs/ai-gateway/1.2.0/llm-proxy/prompt-management/prompt-template.md new file mode 100644 index 000000000..339c30d0e --- /dev/null +++ b/en/docs/ai-gateway/1.2.0/llm-proxy/prompt-management/prompt-template.md @@ -0,0 +1,290 @@ +--- +title: "Prompt Template" +description: "Replace 'template://' URI patterns in LLM request payloads with predefined, parameterized prompt templates." +canonical_url: https://wso2.com/api-platform/docs/ai-gateway/llm-proxy/prompt-management/prompt-template/ +md_url: https://wso2.com/api-platform/docs/ai-gateway/llm-proxy/prompt-management/prompt-template.md +tags: + - ai-gateway + - llm + - prompt-management +author: WSO2 API Platform Documentation Team +last_updated: 2026-06-16 +content_type: "reference" +--- + +# Prompt Template + +## Overview + +The Prompt Template policy enables dynamic prompt transformation by replacing `template://` URI patterns in JSON payloads with predefined templates. Template placeholders are resolved using parameters passed in the URI query string, allowing you to standardize and reuse prompts across different API calls. This is particularly useful for AI/LLM APIs where consistent prompt formatting improves response quality and maintainability. + +## Features + +- Pattern-based template matching using `template://` URI format +- Parameter substitution with `[[parameter-name]]` placeholder syntax +- Multiple templates per policy configuration +- JSON-safe string replacement and escaping +- Processes entire JSON payload as string to find and replace patterns + +## Configuration + +### Parameters + +| Parameter | Type | Required | Default | Description | +|-----------|------|----------|---------|-------------| +| `promptTemplateConfig` | string | Yes | - | JSON string containing an array of template objects. Each template must have a `name` and `prompt` field. Example: `[{"name": "translate", "prompt": "Translate from [[from]] to [[to]]: [[text]]"}]` | + +### Template Configuration Format + +The `promptTemplateConfig` must be a JSON array of template objects: + +```json +[ + { + "name": "template-name", + "prompt": "Template prompt with [[placeholder]] syntax" + } +] +``` + +Each template object contains: +- **name**: Unique identifier for the template (used in `template://` URIs) +- **prompt**: The template string with `[[parameter-name]]` placeholders that will be replaced + +## Template Syntax + +### Template URI Format + +Templates are referenced in JSON payloads using the following URI format: + +``` +template://?=&= +``` + +Example: +``` +template://translate?from=english&to=spanish&text=Hello world +``` + +### Placeholder Syntax + +Within template prompts, use double square brackets to define placeholders: + +``` +[[parameter-name]] +``` + +During resolution, placeholders are replaced with values from the URI query parameters. Parameter names are case-sensitive and must match exactly between the placeholder and the URI parameter. + +Example template: +``` +Translate the following text from [[from]] to [[to]]: [[text]] +``` + +When called with `template://translate?from=english&to=spanish&text=Hello`, the resolved prompt would be: +``` +Translate the following text from english to spanish: Hello +``` + +## Examples + +### Example 1: Translation Template + +Deploy an LLM provider with a translation prompt template: + +```bash +curl -X POST http://localhost:9090/api/management/v0.9/llm-providers \ + -H "Content-Type: application/yaml" \ + -u "$ADMIN_USERNAME:$ADMIN_PASSWORD" \ + --data-binary @- <<'EOF' +apiVersion: gateway.api-platform.wso2.com/v1alpha1 +kind: LlmProvider +metadata: + name: translation-provider +spec: + displayName: Translation Provider + version: v1.0 + template: openai + vhost: openai + upstream: + url: "https://api.openai.com/v1" + auth: + type: api-key + header: Authorization + value: Bearer + accessControl: + mode: deny_all + exceptions: + - path: /chat/completions + methods: [POST] + policies: + - name: prompt-template + version: v1 + paths: + - path: /chat/completions + methods: [POST] + params: + promptTemplateConfig: '[{"name": "translate", "prompt": "Translate the following text from [[from]] to [[to]]: [[text]]"}]' +EOF +``` + +**Test the template:** + +**Note**: Ensure that "openai" is mapped to the appropriate IP address (e.g., 127.0.0.1) in your `/etc/hosts` file, or remove the vhost from the LLM provider configuration and use localhost to invoke. + +```bash +curl -X POST http://openai:8080/chat/completions \ + -H "Content-Type: application/json" \ + -H "Host: openai" \ + -d '{ + "model": "gpt-4", + "messages": [ + { + "role": "user", + "content": "template://translate?from=english&to=spanish&text=Hello world" + } + ] + }' +``` + +The policy will transform the request to: + +```json +{ + "model": "gpt-4", + "messages": [ + { + "role": "user", + "content": "Translate the following text from english to spanish: Hello world" + } + ] +} +``` + +### Example 2: Summarization Template + +Create a template for summarizing content with configurable length: + +```bash +curl -X POST http://localhost:9090/api/management/v0.9/llm-providers \ + -H "Content-Type: application/yaml" \ + -u "$ADMIN_USERNAME:$ADMIN_PASSWORD" \ + --data-binary @- <<'EOF' +apiVersion: gateway.api-platform.wso2.com/v1alpha1 +kind: LlmProvider +metadata: + name: summarization-provider +spec: + displayName: Summarization Provider + version: v1.0 + template: openai + vhost: openai + upstream: + url: "https://api.openai.com/v1" + auth: + type: api-key + header: Authorization + value: Bearer + accessControl: + mode: deny_all + exceptions: + - path: /chat/completions + methods: [POST] + policies: + - name: prompt-template + version: v1 + paths: + - path: /chat/completions + methods: [POST] + params: + promptTemplateConfig: '[{"name": "summarize", "prompt": "Summarize the following content in [[length]] words: [[content]]"}]' +EOF +``` + +**Test with template:** + +```bash +curl -X POST http://openai:8080/chat/completions \ + -H "Content-Type: application/json" \ + -H "Host: openai" \ + -d '{ + "model": "gpt-4", + "messages": [ + { + "role": "user", + "content": "template://summarize?length=50&content=Artificial intelligence is a branch of computer science that aims to create intelligent machines capable of performing tasks that typically require human intelligence." + } + ] + }' +``` + +### Example 3: Multiple Templates + +Configure multiple templates in a single policy: + +```yaml +policies: + - name: prompt-template + version: v1 + paths: + - path: /chat/completions + methods: [POST] + params: + promptTemplateConfig: | + [ + { + "name": "translate", + "prompt": "Translate from [[from]] to [[to]]: [[text]]" + }, + { + "name": "summarize", + "prompt": "Summarize in [[length]] words: [[content]]" + }, + { + "name": "explain", + "prompt": "Explain [[topic]] to a [[audience]] audience: [[question]]" + } + ] +``` + +## Use Cases + +1. **Standardized Prompts**: Ensure consistent prompt formatting across different API consumers by centralizing prompt definitions. + +2. **Reusable Templates**: Create library of common prompts (translation, summarization, explanation) that can be reused across multiple APIs. + +3. **Parameterized Prompts**: Allow dynamic content insertion while maintaining consistent prompt structure and quality. + +4. **Multi-language Support**: Use templates with language parameters to standardize prompts for different locales. + +5. **Prompt Versioning**: Update prompt templates centrally without requiring changes to client applications. + +## Template Pattern Matching + +The policy uses regex pattern matching to find `template://` URIs in the JSON payload: + +- **Pattern**: `template://[a-zA-Z0-9_-]+\?[^\s"']*` +- **Location**: Searches the entire JSON payload as a string +- **Replacement**: Each matched pattern is replaced with the resolved template string (JSON-escaped) + +### Pattern Details + +- Template names can contain letters, numbers, underscores, and hyphens +- Query parameters can contain any characters except spaces, quotes, or single quotes +- Multiple template:// patterns can exist in a single payload +- Each pattern is resolved independently + +## Error Handling + +If a template:// pattern references a template name that doesn't exist in the configuration, the pattern is left unchanged (no replacement occurs). This allows for graceful handling of missing templates. + +When template resolution fails (e.g., invalid JSON escaping), the specific pattern is skipped and other patterns continue to be processed. + +## Notes + +- Template names are case-sensitive and must match exactly between the URI reference and the configuration. +- Parameter names in placeholders `[[param]]` are case-sensitive and must match query parameter names exactly. +- Query parameter values are URL-decoded before being inserted into templates. +- The resolved template string is JSON-escaped (special characters like quotes, newlines are escaped) before replacement. +- The policy processes the entire JSON payload as a string, so templates can be used anywhere in the JSON structure. +- Multiple `template://` patterns can appear in a single payload and will all be processed. diff --git a/en/docs/ai-gateway/1.2.0/llm-proxy/quick-start-guide.md b/en/docs/ai-gateway/1.2.0/llm-proxy/quick-start-guide.md new file mode 100644 index 000000000..b2a992946 --- /dev/null +++ b/en/docs/ai-gateway/1.2.0/llm-proxy/quick-start-guide.md @@ -0,0 +1,229 @@ +--- +title: "LLM Proxy Quick Start Guide" +description: "Run API Platform AI Gateway with Docker Compose, configure an LLM provider, and route your first LLM request through a managed proxy." +canonical_url: https://wso2.com/api-platform/docs/ai-gateway/llm-proxy/quick-start-guide/ +md_url: https://wso2.com/api-platform/docs/ai-gateway/llm-proxy/quick-start-guide.md +tags: + - ai-gateway + - llm + - quickstart +author: WSO2 API Platform Documentation Team +last_updated: 2026-08-05 +content_type: "quickstart" +--- + +## Quick Start + +!!! info "Watch the video walkthrough" + [Check out this quick start on YouTube](https://youtu.be/p5xBXZWt5GU?rel=0) or watch below. + + + +### Using Docker Compose (Recommended) + + +### Prerequisites + +A Docker-compatible container runtime such as: + +- Docker Desktop (Windows / macOS) +- Podman Desktop or Podman (Windows / macOS / Linux) +- Rancher Desktop (Windows / macOS) +- Colima (macOS) +- Docker Engine + Compose plugin (Linux) + +These examples use `docker compose`. If you use another Compose-compatible runtime, use the equivalent commands. + +Verify the commands for your runtime are available. For Docker: + +```bash +docker --version +docker compose version +``` + + +```bash +# Download distribution. +wget https://github.com/wso2/api-platform/releases/download/ai-gateway/v1.2.0-rc2/wso2apip-ai-gateway-1.2.0-rc2.zip + +# Unzip the downloaded distribution. +unzip wso2apip-ai-gateway-1.2.0-rc2.zip + +cd wso2apip-ai-gateway-1.2.0/ + +# Run the one-time setup. This provisions the AES-256 at-rest encryption key, the router HTTPS +# listener certificate, api-platform.env, and the gateway-controller admin credentials. It prints +# the admin password once — copy it. +./scripts/setup.sh + +# Export the admin credentials so the management-API calls below can authenticate. +# The username defaults to "admin"; use the password setup.sh just printed. +export ADMIN_USERNAME=admin +export ADMIN_PASSWORD='' + +# Start the complete stack +docker compose up + +# Verify gateway controller admin endpoint is running +curl http://localhost:9094/api/admin/v1/health +``` + +!!! tip "Port 8080, 8443, 9090, or 9094 already taken?" + If the start command fails with a port binding error, identify what is already listening on the default ports: + + On macOS or Linux, run: + + ```bash + lsof -nP -iTCP:8080 -sTCP:LISTEN + lsof -nP -iTCP:8443 -sTCP:LISTEN + lsof -nP -iTCP:9090 -sTCP:LISTEN + lsof -nP -iTCP:9094 -sTCP:LISTEN + ``` + + On Windows PowerShell, run: + + ```powershell + Get-NetTCPConnection -State Listen -LocalPort 8080,8443,9090,9094 | Select-Object LocalAddress, LocalPort, OwningProcess + ``` + + Stop the conflicting service if you don't need it. If you need to keep it running, change the host-side value of the relevant `ports:` mapping in `docker-compose.yaml`. Then use the remapped host port in the verification and test commands on this page. + +!!! note "Running on Windows" + The commands above assume a Linux/macOS shell. On Windows, run the one-time setup with the PowerShell script instead — it takes the same flags and provisions the same files: + + ```powershell + powershell -ExecutionPolicy Bypass -File .\scripts\setup.ps1 + ``` + + Then set the admin credentials with `$env:ADMIN_USERNAME='admin'` and `$env:ADMIN_PASSWORD=''` in place of the `export` lines. + + The remaining `curl` commands on this page pipe their YAML payload in through a shell heredoc (`--data-binary @- <<'EOF'`), which PowerShell does not support. Either run them from Git Bash or WSL, or save the YAML between `EOF` markers to a file and post that file explicitly — note the `.exe`, since `curl` is an alias for `Invoke-WebRequest` in Windows PowerShell: + + ```powershell + curl.exe -X POST http://localhost:9090/api/management/v1/llm-providers ` + -H "Content-Type: application/yaml" ` + -u "${env:ADMIN_USERNAME}:${env:ADMIN_PASSWORD}" ` + --data-binary "@openai-provider.yaml" + ``` + +## Deploy an OpenAI LLM provider configuration + +The API Platform Gateway currently includes first-class support for the OpenAI LLM provider. As a platform administrator, replace `` with your openai API key and run the following command to deploy a sample OpenAI LLM provider. + +```bash +curl -X POST http://localhost:9090/api/management/v1/llm-providers \ + -H "Content-Type: application/yaml" \ + -u "$ADMIN_USERNAME:$ADMIN_PASSWORD" \ + --data-binary @- <<'EOF' +apiVersion: gateway.api-platform.wso2.com/v1 +kind: LlmProvider +metadata: + name: openai-provider +spec: + displayName: OpenAI Provider + version: v1.0 + template: openai + context: /openai/latest + upstream: + url: https://api.openai.com/v1 + auth: + type: api-key + header: Authorization + value: + accessControl: + mode: deny_all + exceptions: + - path: /chat/completions + methods: [POST] + - path: /models + methods: [GET] + - path: /models/{modelId} + methods: [GET] +EOF +``` + +To test LLM provider traffic routing through the gateway, invoke the following request. + +```bash +curl -X POST https://localhost:8443/openai/latest/chat/completions \ + -H "Content-Type: application/json" \ + -d '{ + "model": "gpt-4o-mini", + "messages": [ + { + "role": "user", + "content": "Hi" + } + ] + }' -k +``` + +## Deploy an LLM proxy configuration to consume an LLM provider + +The API Platform Gateway provides first-class support for configuring and deploying LLM proxies. As an AI developer, run the following command to deploy a sample LLM proxy that consumes the OpenAI LLM provider previously deployed by the platform administrator. + +```bash +curl -X POST http://localhost:9090/api/management/v1/llm-proxies \ + -H "Content-Type: application/yaml" \ + -u "$ADMIN_USERNAME:$ADMIN_PASSWORD" \ + --data-binary @- <<'EOF' +apiVersion: gateway.api-platform.wso2.com/v1 +kind: LlmProxy +metadata: + name: openai-assistant +spec: + displayName: OpenAI Assistant + version: v1.0 + context: /assistant + provider: + id: openai-provider + policies: [] +EOF +``` + +To test LLM proxy traffic routing through the gateway and consume the LLM provider, invoke the following request. + +```bash +curl -X POST "https://localhost:8443/assistant/chat/completions" \ + -H "Content-Type: application/json" \ + -d '{ + "model": "gpt-4o-mini", + "messages": [ + { + "role": "user", + "content": "Hi" + } + ] + }' -k +``` + +## View the LLM provider and proxy in AI Workspace + +The gateway syncs the artifacts you deploy on it up to [AI Workspace](../../../next/ai-workspace/overview.md), the control plane for AI traffic across your organization. The OpenAI provider and the `openai-assistant` proxy you deployed above appear there without being re-declared. See [Manage Gateway-deployed AI artifacts in AI Workspace](../../../next/ai-workspace/sync-gateway-created-artifacts.md). + +## Stopping the Gateway + +When stopping the gateway, you have two options: + +### Option 1: Stop runtime, keep data (persisted proxies and configuration) + +```bash +docker compose down +``` + +This stops the containers but preserves the `controller-data` volume. When you restart with `docker compose up`, all your API configurations will be restored. + +### Option 2: Complete shutdown with data cleanup (fresh start) + +```bash +docker compose down -v +``` +This stops containers and removes the `controller-data` volume. Next startup will be a clean slate with no persisted templates or provider configuration. diff --git a/en/docs/ai-gateway/1.2.0/llm-proxy/semantic-caching.md b/en/docs/ai-gateway/1.2.0/llm-proxy/semantic-caching.md new file mode 100644 index 000000000..38ef94ffb --- /dev/null +++ b/en/docs/ai-gateway/1.2.0/llm-proxy/semantic-caching.md @@ -0,0 +1,305 @@ +--- +title: "Semantic Caching" +description: "Cache LLM responses using vector similarity search so semantically similar queries are served from cache, reducing cost and latency." +canonical_url: https://wso2.com/api-platform/docs/ai-gateway/llm-proxy/semantic-caching/ +md_url: https://wso2.com/api-platform/docs/ai-gateway/llm-proxy/semantic-caching.md +tags: + - ai-gateway + - llm + - caching +author: WSO2 API Platform Documentation Team +last_updated: 2026-06-16 +content_type: "reference" +--- + +# Semantic Caching + +## Overview + +The Semantic Cache policy enables intelligent response caching for LLM (Large Language Model) APIs using vector similarity search. Unlike traditional key-based caching, semantic caching understands the meaning of requests and can serve cached responses for semantically similar queries, even when the exact wording differs. This dramatically improves performance and reduces costs by avoiding redundant API calls to upstream LLM services. + +The policy uses embedding models to convert request text into high-dimensional vectors, then performs similarity searches in a vector database to find previously cached responses. If a similar request is found within the configured similarity threshold, the cached response is returned immediately without calling the upstream service. + +## Features + +- **Vector-based similarity matching**: Uses embeddings to find semantically similar requests, not just exact matches +- **Multiple embedding provider support**: Works with OpenAI, Mistral, and Azure OpenAI embedding services +- **Multiple vector database support**: Supports Redis and Milvus as vector storage backends +- **Configurable similarity threshold**: Control cache hit sensitivity (0.0 to 1.0) +- **JSONPath extraction**: Extract specific fields from request body for embedding generation +- **Automatic cache management**: Stores successful responses (200) automatically after upstream calls +- **Immediate response on cache hit**: Returns cached response with `X-Cache-Status: HIT` header without upstream call +- **TTL support**: Configurable time-to-live for cache entries + +## How It Works + +### Request Phase + +1. **Text Extraction**: Extracts text from the request body using JSONPath (if configured) or uses the entire request body +2. **Embedding Generation**: Generates a vector embedding from the extracted text using the configured embedding provider +3. **Cache Lookup**: Searches the vector database for semantically similar cached responses using cosine similarity +4. **Threshold Check**: If a similar embedding is found with similarity >= similarityThreshold, returns the cached response immediately +5. **Cache Miss**: If no similar response is found, the request proceeds to the upstream service + +### Response Phase + +1. **Success Check**: Only processes responses with 200 status codes +2. **Embedding Retrieval**: Retrieves the embedding generated during the request phase from metadata +3. **Response Storage**: Stores the response payload along with its embedding in the vector database +4. **TTL Application**: Applies the configured TTL to the cache entry + +## Configuration + +### Policy Parameters + +| Parameter | Type | Required | Default | Description | +|-----------|------|----------|---------|-------------| +| `similarityThreshold` | number | Yes | - | Similarity threshold for cache hits (0.0 to 1.0). Higher values require more similarity. For example, 0.9 means 90% similarity required. Recommended: 0.85-0.95 for strict matching, 0.70-0.85 for more flexible matching. | +| `jsonPath` | string | No | `""` | JSONPath expression to extract text from request body for embedding generation. If empty, uses the entire request body. Example: `"$.messages[0].content"` to extract the first message's content. | + +### System Parameters (Required) + +These parameters are typically configured at the gateway level and automatically injected, or you can override those values from the params section in the api artifact definition file as well: + +#### Embedding Provider Configuration + +| Parameter | Type | Required | Description | +|-----------|------|----------|-------------| +| `embeddingProvider` | string | Yes | Embedding provider type. Must be one of: `OPENAI`, `MISTRAL`, `AZURE_OPENAI` | +| `embeddingEndpoint` | string | Yes | Endpoint URL for the embedding service. Examples: OpenAI: `https://api.openai.com/v1/embeddings`, Mistral: `https://api.mistral.ai/v1/embeddings`, Azure OpenAI: Your Azure OpenAI endpoint URL | +| `embeddingModel` | string | Conditional | - | Embedding model name. **Required for OPENAI and MISTRAL**, not required for AZURE_OPENAI (deployment name is in endpoint URL). Examples: OpenAI: `text-embedding-ada-002` or `text-embedding-3-small`, Mistral: `mistral-embed` | +| `embeddingDimension` | integer | Yes | Dimension of embedding vectors. Common values: 1536 (OpenAI ada-002), 1024 (Mistral). Must match the model's output dimension. | +| `apiKey` | string | Yes | API key for the embedding service authentication. The authentication header is automatically set to `api-key` for Azure OpenAI and `Authorization` for other providers. | + +#### Vector Database Configuration + +| Parameter | Type | Required | Description | +|-----------|------|----------|-------------| +| `vectorStoreProvider` | string | Yes | Vector database provider. Must be one of: `REDIS`, `MILVUS` | +| `dbHost` | string | Yes | Vector database host address | +| `dbPort` | integer | Yes | Vector database port number | +| `username` | string | No | Database username for authentication (if required) | +| `password` | string | No | Database password for authentication (if required) | +| `database` | string | No | Database name or index number (for Redis) | +| `ttl` | integer | No | Time-to-live for cache entries in seconds. Default is 3600 (1 hour). Set to 0 for no expiration. | + + +### Configuring System Parameters in config.toml + +System parameters can be configured globally in the gateway's `config.toml` file. These values serve as defaults for all Semantic Cache policy instances and can be overridden per-policy in the API configuration if needed. + +#### Location in config.toml + +Add the following configuration section to your `config.toml` file: + +```toml +embedding_provider = "MISTRAL" # Supported: MISTRAL, OPENAI, AZURE_OPENAI +embedding_provider_endpoint = "https://api.mistral.ai/v1/embeddings" +embedding_provider_model = "mistral-embed" +embedding_provider_dimension = 1024 +embedding_provider_api_key = "" + +vector_db_provider = "REDIS" # Supported: REDIS, MILVUS +vector_db_provider_host = "redis" +vector_db_provider_port = 6379 +vector_db_provider_database = "0" +vector_db_provider_username = "default" +vector_db_provider_password = "default" +vector_db_provider_ttl = 3600 +``` + +## JSONPath Support + +The policy supports JSONPath expressions to extract specific text from request bodies before generating embeddings. This is useful for: +- Extracting message content from chat completion requests +- Focusing on specific prompt fields while ignoring metadata +- Handling structured JSON payloads + +### Common JSONPath Examples + +- `$.messages[0].content` - First message's content in chat completions +- `$.messages[-1].content` - Last message's content +- `$.prompt` - Extract prompt field from completions API +- `$.input` - Extract input field from embeddings API +- `$` - Entire request body (default if jsonPath is not specified) + +## Examples + +### Example 1: OpenAI Embeddings with Redis + +Deploy an LLM provider with semantic caching using OpenAI embeddings and Redis vector store: + +```bash +curl -X POST http://localhost:9090/api/management/v0.9/llm-providers \ + -H "Content-Type: application/yaml" \ + -u "$ADMIN_USERNAME:$ADMIN_PASSWORD" \ + --data-binary @- <<'EOF' +apiVersion: gateway.api-platform.wso2.com/v1alpha1 +kind: LlmProvider +metadata: + name: cached-chat-provider +spec: + displayName: OpenAI Cached Provider + version: v1.0 + template: openai + vhost: openai + upstream: + url: "https://api.openai.com/v1" + auth: + type: api-key + header: Authorization + value: Bearer + accessControl: + mode: deny_all + exceptions: + - path: /chat/completions + methods: [POST] + policies: + - name: semantic-cache + version: v1 + paths: + - path: /chat/completions + methods: [POST] + params: + similarityThreshold: 0.85 + jsonPath: "$.messages[0].content" +EOF +``` + +**Test the semantic cache:** + +**Note**: Ensure that "openai" is mapped to the appropriate IP address (e.g., 127.0.0.1) in your `/etc/hosts` file, or remove the vhost from the LLM provider configuration and use localhost to invoke. + +```bash +# First request - cache miss, will call upstream +curl -X POST http://openai:8080/chat/completions \ + -H "Content-Type: application/json" \ + -H "Host: openai" \ + -d '{ + "model": "gpt-4", + "messages": [ + { + "role": "user", + "content": "Explain quantum computing in simple terms" + } + ] + }' + +# Second request with similar but different wording - cache hit! +curl -X POST http://openai:8080/chat/completions \ + -H "Content-Type: application/json" \ + -H "Host: openai" \ + -d '{ + "model": "gpt-4", + "messages": [ + { + "role": "user", + "content": "Can you describe quantum computing using simple language?" + } + ] + }' +# Response will include: X-Cache-Status: HIT +``` + +## Use Cases + +1. **Cost Reduction**: Reduce API costs by serving cached responses for similar queries, especially valuable for expensive LLM API calls. + +2. **Latency Improvement**: Return cached responses in milliseconds instead of waiting for LLM processing (typically 1-5 seconds), significantly improving user experience. + +3. **Rate Limit Management**: Reduce the number of upstream API calls, helping stay within rate limits and avoiding throttling. + +4. **Consistency**: Ensure users receive consistent responses for semantically equivalent queries, improving predictability. + +5. **Offline Resilience**: Serve cached responses even when upstream services experience temporary outages. + +6. **A/B Testing**: Compare cached vs. fresh responses to evaluate prompt engineering improvements. + +7. **Development/Testing**: Speed up development cycles by reusing cached responses during testing. + +8. **High-Traffic Applications**: Handle high volumes of similar queries efficiently without overwhelming upstream services. + +## Similarity Threshold Guidelines + +The `similarityThreshold` parameter controls how similar requests must be to trigger a cache hit: + +- **0.95-1.0**: Very strict matching. Only near-identical requests will hit cache. Use for exact-match scenarios. +- **0.85-0.94**: Recommended for most use cases. Catches semantically equivalent requests with some wording variation. +- **0.75-0.84**: More flexible matching. Useful for broader conceptual similarity. +- **0.60-0.74**: Very flexible. May return cached responses for loosely related queries. +- **Below 0.60**: Not recommended. Risk of returning irrelevant cached responses. + +**Recommendation**: Start with 0.85 and adjust based on your use case. Monitor cache hit rates and response relevance to fine-tune. + +## Cache Behavior + +### Cache Hit + +When a similar request is found: +- Returns cached response immediately (no upstream call) +- Adds `X-Cache-Status: HIT` header to response +- Status code: 200 (from cached response) +- Response time: Typically < 50ms (vs. 1-5 seconds for LLM APIs) + +### Cache Miss + +When no similar request is found: +- Request proceeds to upstream service normally +- Response is cached after successful upstream call (200 status) +- Subsequent similar requests may hit the cache + +### Cache Storage + +- Only successful responses (200 status code) are cached +- Responses are stored with their embeddings in the vector database +- TTL is applied to all cache entries +- Each route/API maintains a separate cache namespace to avoid cross-contamination + +## Error Handling + +The policy is designed to be resilient: + +- **Embedding Generation Failure**: If embedding generation fails, the request proceeds to upstream (cache is skipped) +- **Vector Database Unavailable**: If the vector database is unreachable, requests proceed to upstream +- **Cache Storage Failure**: If storing a response fails, the response is still returned to the client (cache operation is non-blocking) +- **Invalid JSONPath**: If JSONPath extraction fails, the entire request body is used for embedding generation + +These behaviors ensure that caching failures do not block legitimate requests. The system gracefully degrades to direct upstream calls when caching is unavailable. + +## Performance Considerations + +1. **Embedding Generation Latency**: Generating embeddings adds ~100-500ms to request processing. This is offset by cache hits that save 1-5 seconds. + +2. **Vector Database Performance**: + - Redis with RedisSearch: Fast queries, good for smaller datasets (< 1M vectors) + - Milvus: Optimized for large-scale vector search, better for > 1M vectors + +3. **Cache Hit Rate**: Aim for 20-40% cache hit rate for cost-effective caching. Below 10% may not justify the overhead. + +4. **Embedding Dimension**: Higher dimensions (e.g., 1536) provide better accuracy but increase storage and search time. Choose based on your quality requirements. + +5. **Index Creation**: Vector database indexes are created automatically on first use. This may take a few seconds for large datasets. + +## Notes + +- The policy requires both request and response phases to function properly (generates embeddings in request phase, stores responses in response phase). + +- Embedding generation adds latency to each request (~100-500ms). This overhead is typically offset by the performance gains from cache hits. + +- Cache entries are scoped per route/API to prevent cross-contamination between different APIs or routes. + +- Only responses with 200 status code are cached. Errors and non-200 responses are never cached. + +- The similarity search uses cosine similarity to compare embeddings. This is optimal for semantic similarity matching. + +- Vector database indexes are created automatically when the policy is first used. Ensure your vector database has sufficient resources. + +- The policy maintains provider instances per route for efficiency. Configuration changes require policy reinitialization. + +- TTL of 0 means no expiration. Use with caution as it may lead to unbounded cache growth. + +- JSONPath extraction is optional. If not specified, the entire request body (as string) is used for embedding generation. + +- The policy stores embeddings in metadata between request and response phases. Ensure metadata persistence is enabled in your gateway configuration. + +- For production deployments, monitor cache hit rates, embedding generation latency, and vector database performance metrics to optimize configuration. diff --git a/en/docs/ai-gateway/1.2.0/mcp-proxy/policies/mcp-acl-list.md b/en/docs/ai-gateway/1.2.0/mcp-proxy/policies/mcp-acl-list.md new file mode 100644 index 000000000..377d3fb41 --- /dev/null +++ b/en/docs/ai-gateway/1.2.0/mcp-proxy/policies/mcp-acl-list.md @@ -0,0 +1,175 @@ +--- +title: "MCP ACL List" +description: "Control access to MCP tools, resources, and prompts using allow or deny mode with exceptions in API Platform AI Gateway." +canonical_url: https://wso2.com/api-platform/docs/ai-gateway/mcp-proxy/policies/mcp-acl-list/ +md_url: https://wso2.com/api-platform/docs/ai-gateway/mcp-proxy/policies/mcp-acl-list.md +tags: + - ai-gateway + - mcp + - policies +author: WSO2 API Platform Documentation Team +last_updated: 2026-06-16 +content_type: "reference" +--- + +# MCP ACL List + +## Overview + +The MCP ACL List policy provides access control for Model Context Protocol (MCP) tools, resources, and prompts using an allow/deny mode with exceptions. This policy filters list responses and enforces access rules on request paths based on configured mode and exceptions. Unlike the [MCP Rewrite policy](./mcp-rewrite.md), this policy does not rewrite capability names or modify list entry contents—it purely controls visibility and access. + +The policy operates on three types of MCP capabilities: tools, resources, and prompts. For each type, you can specify a mode (allow or deny) and a list of exceptions. Requests for capabilities not matching the access control rules are rejected with an appropriate error. + +## Features + +- **Tool-Level Access Control**: Allow or deny access to specific tools using allow/deny mode with exceptions. +- **Resource-Level Access Control**: Control access to specific resources (identified by URI) using flexible ACL rules. +- **Prompt-Level Access Control**: Manage access to specific prompts using configurable access modes. +- **Flexible ACL Modes**: Support both allow-with-exceptions and deny-with-exceptions patterns. +- **List Filtering**: Filter list responses to only include capabilities that match the access control rules. +- **Request Path Enforcement**: Enforce the same allow/deny rules on request paths, rejecting access to denied capabilities. + +## Configuration + +The MCP ACL List policy uses a single-level configuration model where all parameters are configured per-MCP-API/route in the API definition YAML. + +### User Parameters (API Definition) + +These parameters are configured per MCP Proxy by the API developer: + +| Parameter | Type | Required | Description | +|-----------|------|----------|-------------| +| `tools` | object | No | ACL configuration for tools with `mode` (allow or deny) and optional `exceptions` list. | +| `tools.mode` | string | Yes | ACL mode for tools: "allow" (allow all except exceptions) or "deny" (deny all except exceptions). | +| `tools.exceptions` | array | No | List of tool names that are exceptions to the mode (1-256 characters each). | +| `resources` | object | No | ACL configuration for resources with `mode` (allow or deny) and optional `exceptions` list. | +| `resources.mode` | string | Yes | ACL mode for resources: "allow" (allow all except exceptions) or "deny" (deny all except exceptions). | +| `resources.exceptions` | array | No | List of resource URIs that are exceptions to the mode (1-2048 characters each). | +| `prompts` | object | No | ACL configuration for prompts with `mode` (allow or deny) and optional `exceptions` list. | +| `prompts.mode` | string | Yes | ACL mode for prompts: "allow" (allow all except exceptions) or "deny" (deny all except exceptions). | +| `prompts.exceptions` | array | No | List of prompt names that are exceptions to the mode (1-256 characters each). | + +## Access Control Logic + +For each capability type (tools, resources, prompts): + +- **Missing capability config**: All capabilities of that type are allowed (no restrictions). +- **mode: allow, exceptions: [...]**: Allow all capabilities except those listed in exceptions. +- **mode: deny, exceptions: [...]**: Deny all capabilities except those listed in exceptions. + +## MCP Proxy Definition Examples + +### Example 1: Deny Specific Tools + +Deny access to certain tools while allowing all others: + +```yaml +apiVersion: gateway.api-platform.wso2.com/v1alpha1 +kind: Mcp +metadata: + name: mcp-server-api-v1.0 +spec: + displayName: mcp-server-api + version: v1.0 + context: /mcpserver + upstream: + url: https://mcp-backend:8080 + policies: + - name: mcp-acl-list + version: v0.1.0 + params: + tools: + mode: allow + exceptions: + - delete-all + - drop-database + tools: + ... +``` + +### Example 2: Allow Only Specific Resources + +Allow access to only whitelisted resources: + +```yaml +apiVersion: gateway.api-platform.wso2.com/v1alpha1 +kind: Mcp +metadata: + name: mcp-server-api-v1.0 +spec: + displayName: mcp-server-api + version: v1.0 + context: /mcpserver + upstream: + url: https://mcp-backend:8080 + policies: + - name: mcp-acl-list + version: v0.1.0 + params: + resources: + mode: deny + exceptions: + - file:///public/documents + - file:///public/images + resources: + ... +``` + +### Example 3: Mixed Access Control + +Apply different access control rules to different capability types: + +```yaml +apiVersion: gateway.api-platform.wso2.com/v1alpha1 +kind: Mcp +metadata: + name: mcp-server-api-v1.0 +spec: + displayName: mcp-server-api + version: v1.0 + context: /mcpserver + upstream: + url: https://mcp-backend:8080 + policies: + - name: mcp-acl-list + version: v0.1.0 + params: + tools: + mode: allow + exceptions: + - admin-only-tool + - deprecated-tool + resources: + mode: allow + exceptions: + - file:///internal-resources + prompts: + mode: deny + exceptions: + - standard-prompt + - approved-prompt + tools: + ... +``` + +## Use Cases + +1. **Sensitive Operation Blocking**: Deny access to tools or resources that perform sensitive operations (e.g., delete, modify system configuration). +2. **Public API Restriction**: Allow only specific public resources while denying access to internal resources. +3. **Role-Based Access**: Combine this policy with authentication/authorization policies to implement role-based access control. +4. **Gradual Feature Rollout**: Deny access to beta or experimental tools while they are being tested. +5. **Compliance and Security**: Enforce compliance policies by denying access to resources or tools that are not approved for a specific tenant or environment. +6. **Cost Control**: Deny access to expensive or resource-intensive operations. + +## Comparison with MCP Rewrite Policy + +| Aspect | MCP ACL List | MCP Rewrite | +|--------|--------------|-------------| +| **Primary Purpose** | Access control via allow/deny | Capability name mapping | +| **Rewrites Names** | No | Yes | +| **Filters Lists** | Yes | Yes | +| **Enforces Request Paths** | Yes | Yes | +| **Configuration Complexity** | Simple (mode + exceptions) | Detailed (names, descriptions, targets) | +| **Metadata Modification** | No | Yes | + +Both policies can be used together: use MCP ACL List for access control and MCP Rewrite for name mapping. diff --git a/en/docs/ai-gateway/1.2.0/mcp-proxy/policies/mcp-authentication.md b/en/docs/ai-gateway/1.2.0/mcp-proxy/policies/mcp-authentication.md new file mode 100644 index 000000000..edccb7d45 --- /dev/null +++ b/en/docs/ai-gateway/1.2.0/mcp-proxy/policies/mcp-authentication.md @@ -0,0 +1,175 @@ +--- +title: "MCP Authentication" +description: "Secure MCP server traffic by validating JWT access tokens and serving OAuth protected resource metadata in AI Gateway." +canonical_url: https://wso2.com/api-platform/docs/ai-gateway/mcp-proxy/policies/mcp-authentication/ +md_url: https://wso2.com/api-platform/docs/ai-gateway/mcp-proxy/policies/mcp-authentication.md +tags: + - ai-gateway + - mcp + - security +author: WSO2 API Platform Documentation Team +last_updated: 2026-06-16 +content_type: "reference" +--- + +# MCP Authentication + +## Overview + +The MCP Authentication policy is designed to secure traffic to Model Context Protocol (MCP) servers. The Gateway acts as a resource server, protecting MCP resources by validating access tokens presented in requests. This policy leverages the underlying JWT Authentication mechanism for token validation and additionally handles MCP-specific requirements such as serving protected resource metadata. This policy supports the auth requirements mentioned in the [MCP Specification](https://modelcontextprotocol.io/specification/2025-06-18/basic/authorization#introduction). + +## Features + +- **Access Token Validation**: Validates JWT access tokens using configured key managers. Please refer the [JWT Authentication Policy](https://wso2.com/api-platform/policy-hub/policies/jwt-auth) for more information on how the key validation works. +- **Protected Resource Metadata**: Intercepts `GET /.well-known/oauth-protected-resource` requests to return resource metadata, including authorization servers and supported scopes. +- **Standardized Error Handling**: Returns `WWW-Authenticate` headers with `resource_metadata` on authentication failures. +- **Configurable Validation**: Supports issuer, audience, scope, and custom claim validation. +- **Claim Mapping**: Maps token claims to downstream headers. + +## Configuration + +The MCP Authentication policy uses a two-level configuration model: + +- **System Parameters**: Configured by the administrator in `config.toml` under `policy_configurations.mcpauth_v0` or `policy_configurations.jwtauth_v0` depending on the parameter. +- **User Parameters**: Configured per MCP proxy in the configuration yaml. + +### System Parameters (config.toml) + +These parameters are set by the administrator and apply globally to all MCP authentication policies: + +| Parameter | Type | Required | Path | Description | +|-----------|------|----------|----------|-------------| +| `keymanagers` | array | Yes | jwtauth_v0 | List of key manager definitions. Each entry must include a unique `name` and either `jwks` (for remote JWKS or local certificates) configuration. | +| `gatewayhost` | string | No | mcpauth_v0 | The outward-facing gateway host name which will be used when deriving the values related to protected resource metadata in headers and body. The gateway will fall back to this if there are no vhosts defined in the MCP proxy configuration. | + +#### Key Manager Configuration + +Each key manager in the `keymanagers` array supports the following structure: + +| Parameter | Type | Required | Description | +|-----------|------|----------|-------------| +| `name` | string | Yes | Unique name for this key manager (used in user-level `issuers` configuration). | +| `issuer` | string | No | Optional issuer (iss) value associated with keys from this provider. | +| `jwks.remote.uri` | string | Conditional | JWKS endpoint URL. Required if using remote JWKS. | +| `jwks.remote.certificatePath` | string | No | Path to CA certificate file for validating self-signed JWKS endpoints. | +| `jwks.remote.skipTlsVerify` | boolean | No | If true, skip TLS certificate verification. Use with caution. | +| `jwks.local.inline` | string | Conditional | Inline PEM-encoded certificate or public key. | +| `jwks.local.certificatePath` | string | Conditional | Path to certificate or public key file. | + +> **Note**: Either `jwks.remote` or `jwks.local` must be specified, but not both. + +### User Parameters (API Definition) + +These parameters are configured per-API/route by the API developer: + +| Parameter | Type | Required | Default | Description | +|-----------|------|----------|---------|-------------| +| `issuers` | array | No | - | List of issuer names (referencing entries in `system.keymanagers`). This list is sent as `authorization_servers` in the protected resource metadata response. If omitted, all configured key managers are used. | +| `requiredScopes` | array | No | - | List of scopes that should be included in the token. These are also advertised in the protected resource metadata. | +| `audiences` | array | No | - | List of acceptable audience values; token must contain at least one. | +| `requiredClaims` | object | No | - | Map of claimName → expectedValue for custom claim validation. | +| `claimMappings` | object | No | - | Map of claimName → downstream header name to expose claims for downstream services. | + +## System Configuration Example + +Add the following to your `gateway/configs/config.toml` file under `policy_configurations`: + +```toml +[policy_configurations.mcpauth_v0] +gatewayhost = "gw.example.com" + +[policy_configurations.jwtauth_v0] +jwkscachettl = "5m" +jwksfetchtimeout = "5s" +jwksfetchretrycount = 3 +jwksfetchretryinterval = "2s" +allowedalgorithms = ["RS256", "ES256"] +leeway = "30s" +authheaderscheme = "Bearer" +headername = "Authorization" +onfailurestatuscode = 401 +errormessageformat = "json" +errormessage = "Authentication failed." +validateissuer = true + +[[policy_configurations.jwtauth_v0.keymanagers]] +name = "PrimaryIDP" +issuer = "https://idp.example.com/oauth2/token" + +[policy_configurations.jwtauth_v0.keymanagers.jwks.remote] +uri = "https://idp.example.com/oauth2/jwks" +skipTlsVerify = false + +[[policy_configurations.jwtauth_v0.keymanagers]] +name = "SecondaryIDP" +issuer = "https://auth.example.org/oauth2/token" + +[policy_configurations.jwtauth_v0.keymanagers.jwks.remote] +uri = "https://auth.example.org/oauth2/jwks" +skipTlsVerify = false +``` + +## MCP Proxy Definition Examples + +### Example 1: Basic MCP Authentication + +Apply MCP authentication to an API using a specific key manager: + +```yaml +apiVersion: gateway.api-platform.wso2.com/v1alpha1 +kind: Mcp +metadata: + name: mcp-server-api-v1.0 +spec: + displayName: mcp-server-api + version: v1.0 + context: /mcpserver + vhost: mcp1.gw.example.com + upstream: + url: https://mcp-backend:8080 + policies: + - name: mcp-auth + version: v0.1.1 + params: + issuers: + - PrimaryIDP + tools: + ... +``` + +### Example 2: Scope and Audience Validation + +Require specific scopes and audiences: + +```yaml +apiVersion: gateway.api-platform.wso2.com/v1alpha1 +kind: Mcp +metadata: + name: mcp-server-api-v1.0 +spec: + displayName: mcp-server-api + version: v1.0 + context: /mcpserver + vhost: mcp1.gw.example.com + upstream: + url: https://mcp-backend:8080 + policies: + - name: mcp-auth + version: v0.1.1 + params: + issuers: + - PrimaryIDP + audiences: + - https://mcp-api.example.com + requiredScopes: + - mcp:read + - mcp:write + tools: + ... +``` + +## Use Cases + +1. **MCP Server Security**: Protect Model Context Protocol servers by requiring valid access tokens from trusted identity providers. +2. **Resource Discovery**: Enable MCP clients to discover authorization requirements (authorization servers and scopes) via the standard `.well-known/oauth-protected-resource` endpoint. +3. **Multi-Provider Support**: Allow MCP clients to authenticate using tokens from different identity providers (e.g., different organizations or tenants). diff --git a/en/docs/ai-gateway/1.2.0/mcp-proxy/policies/mcp-authorization.md b/en/docs/ai-gateway/1.2.0/mcp-proxy/policies/mcp-authorization.md new file mode 100644 index 000000000..35e84f00e --- /dev/null +++ b/en/docs/ai-gateway/1.2.0/mcp-proxy/policies/mcp-authorization.md @@ -0,0 +1,271 @@ +--- +title: "MCP Authorization" +description: "Define fine-grained access control rules for MCP tools, resources, and prompts based on JWT claims and scopes." +canonical_url: https://wso2.com/api-platform/docs/ai-gateway/mcp-proxy/policies/mcp-authorization/ +md_url: https://wso2.com/api-platform/docs/ai-gateway/mcp-proxy/policies/mcp-authorization.md +tags: + - ai-gateway + - mcp + - security +author: WSO2 API Platform Documentation Team +last_updated: 2026-06-16 +content_type: "reference" +--- + +# MCP Authorization + +## Overview + +The MCP Authorization policy provides fine-grained access control for Model Context Protocol (MCP) server resources. It enables API administrators to define authorization rules based on user claims and scopes extracted from validated JWT tokens, controlling access to specific MCP tools, resources, prompts, and JSON-RPC methods. + +> **Prerequisite**: The MCP Authorization policy requires the [MCP Authentication policy](./mcp-authentication.md) to be applied first. The MCP Authentication policy validates and extracts JWT claims that are used by the authorization policy for access control decisions. + +## Features + +- **Tool-Level Access Control**: Restrict access to specific MCP tools based on user claims and scopes +- **Resource-Level Access Control**: Control access to specific MCP resources based on authorization rules +- **Prompt-Level Access Control**: Manage access to specific MCP prompts +- **JSON-RPC Method-Level Access Control**: Apply authorization rules at the JSON-RPC method level (e.g., `tools/call`, `resources/read`, `prompts/get`) for fine-grained control. Only methods under `tools/`, `resources/`, and `prompts/` are evaluated. +- **Flexible Rule-Based Authorization**: Define multiple authorization rules with attribute matching (exact or wildcard) +- **Claim-Based Validation**: Validate custom claims (e.g., department, role, team) in user tokens +- **Scope-Based Validation**: Require specific OAuth scopes for accessing protected resources +- **Wildcard Matching**: Use wildcard patterns ("*") to create default rules for all resources of a type + +## Configuration + +The MCP Authorization policy uses a single-level configuration model where all parameters are configured per-MCP-API/route in the API definition YAML. + +### User Parameters (API Definition) + +These parameters are configured per MCP Proxy by the API developer: + +| Parameter | Type | Required | Default | Description | +|-----------|------|----------|---------|-------------| +| `rules` | array | Yes | - | List of authorization rules that define access control policies for MCP resources. | +| `rules[].attribute` | object | Yes | - | The MCP resource attribute to which the authorization rule applies. | +| `rules[].attribute.type` | string | Yes | - | Type of MCP resource: "tool", "resource", "prompt", "method". | +| `rules[].attribute.name` | string | No | - | Name or identifier of the resource. Use "*" for wildcard matching (applies to all resources of the specified type). Examples: "list_files" for tools, "file:///some_resource" for resources, "weather_summary" for prompts, "tools/call" for methods. | +| `rules[].requiredScopes` | array | No | - | List of OAuth scopes required to access this resource. The token must contain all of the specified scopes. | +| `rules[].requiredClaims` | object | No | - | Map of claim names to expected values. All specified claims must be present in the token with matching values. | + +## MCP Proxy Definition Examples + +### Example 1: Basic Tool Access Control + +Restrict access to specific tools based on scopes: + +```yaml +apiVersion: gateway.api-platform.wso2.com/v1alpha1 +kind: Mcp +metadata: + name: mcp-server-api-v1.0 +spec: + displayName: mcp-server-api + version: v1.0 + context: /mcpserver + vhost: mcp1.gw.example.com + upstream: + url: https://mcp-backend:8080 + policies: + - name: mcp-auth + version: v0.1.1 + params: + issuers: + - PrimaryIDP + - name: mcp-authz + version: v0.1.0 + params: + rules: + - attribute: + type: tool + name: list_files + requiredScopes: + - mcp:tool:read + - attribute: + type: tool + name: create_file + requiredScopes: + - mcp:tool:write + - attribute: + type: tool + name: "*" + requiredScopes: + - mcp:tool:execute + tools: + ... +``` + +### Example 2: Claim-Based Resource Access + +Control resource access based on user claims: + +```yaml +apiVersion: gateway.api-platform.wso2.com/v1alpha1 +kind: Mcp +metadata: + name: mcp-server-api-v1.0 +spec: + displayName: mcp-server-api + version: v1.0 + context: /mcpserver + vhost: mcp1.gw.example.com + upstream: + url: https://mcp-backend:8080 + policies: + - name: mcp-auth + version: v0.1.1 + params: + issuers: + - PrimaryIDP + - name: mcp-authz + version: v0.1.0 + params: + rules: + - attribute: + type: resource + name: "file:///private/main" + requiredClaims: + department: "engineering" + requiredScopes: + - mcp:resource:read + - attribute: + type: resource + name: "file:///public/main" + requiredScopes: + - mcp:resource:read + tools: + ... +``` + +### Example 3: Role-Based Prompt Access + +Restrict prompt access based on user roles: + +```yaml +apiVersion: gateway.api-platform.wso2.com/v1alpha1 +kind: Mcp +metadata: + name: mcp-server-api-v1.0 +spec: + displayName: mcp-server-api + version: v1.0 + context: /mcpserver + vhost: mcp1.gw.example.com + upstream: + url: https://mcp-backend:8080 + policies: + - name: mcp-auth + version: v0.1.1 + params: + issuers: + - PrimaryIDP + - name: mcp-authz + version: v0.1.0 + params: + rules: + - attribute: + type: prompt + name: "admin_summary" + requiredClaims: + role: "admin" + requiredScopes: + - mcp:prompt:admin + - attribute: + type: prompt + name: "*" + requiredScopes: + - mcp:prompt:read + tools: + ... +``` + +### Example 4: Multi-Level Authorization + +Combine different resource types with varying access requirements: + +```yaml +apiVersion: gateway.api-platform.wso2.com/v1alpha1 +kind: Mcp +metadata: + name: mcp-server-api-v1.0 +spec: + displayName: mcp-server-api + version: v1.0 + context: /mcpserver + vhost: mcp1.gw.example.com + upstream: + url: https://mcp-backend:8080 + policies: + - name: mcp-auth + version: v0.1.1 + params: + issuers: + - PrimaryIDP + requiredScopes: + - mcp:access + - name: mcp-authz + version: v0.1.0 + params: + rules: + # Restrictive tool access + - attribute: + type: tool + name: "execute_command" + requiredClaims: + department: "platform" + role: "admin" + requiredScopes: + - mcp:tool:execute:admin + # General tool access + - attribute: + type: tool + name: "*" + requiredScopes: + - mcp:tool:execute + # Resource access for finance department + - attribute: + type: resource + name: "file:///finance/*" + requiredClaims: + department: "finance" + requiredScopes: + - mcp:resource:read:finance + # Public resources + - attribute: + type: resource + name: "*" + requiredScopes: + - mcp:resource:read + tools: + ... +``` + +## Authorization Decision Examples + +**Scenario 1**: User with scope `mcp:tool:read` attempts to call `list_files` tool +- Rule: `attribute.type="tool", attribute.name="list_files", requiredScopes=["mcp:tool:read"]` +- Result: ✅ Access Granted + +**Scenario 2**: User with scope `mcp:tool:execute` (no write scope) attempts to call `create_file` tool +- Rule: `attribute.type="tool", attribute.name="create_file", requiredScopes=["mcp:tool:write"]` +- Result: ❌ Access Denied (insufficient scopes) + +**Scenario 3**: User with claim `department="engineering"` attempts to read resource `file:///private/code` +- Rule: `attribute.type="resource", attribute.name="file:///private/code", requiredClaims={department="engineering"}` +- Result: ✅ Access Granted + +**Scenario 4**: User with claim `department="finance"` (no engineering) attempts to read resource `file:///private/code` +- Rule: `attribute.type="resource", attribute.name="file:///private/code", requiredClaims={department="engineering"}` +- Result: ❌ Access Denied (claim mismatch) + +## Error Handling + +When authorization fails, the policy returns: +- **HTTP Status**: `403 Forbidden` +- **Response Body**: JSON error response with a reason message +- **WWW-Authenticate Header**: Contains information about required scopes for the denied resource + +## Related Policies + +- [MCP Authentication Policy](./mcp-authentication.md) - Validates JWT tokens and is a prerequisite for MCP Authorization +- [JWT Authentication Policy](https://wso2.com/api-platform/policy-hub/policies/jwt-auth) - Base JWT token validation mechanism diff --git a/en/docs/ai-gateway/1.2.0/mcp-proxy/policies/mcp-rewrite.md b/en/docs/ai-gateway/1.2.0/mcp-proxy/policies/mcp-rewrite.md new file mode 100644 index 000000000..1ae86dda2 --- /dev/null +++ b/en/docs/ai-gateway/1.2.0/mcp-proxy/policies/mcp-rewrite.md @@ -0,0 +1,161 @@ +--- +title: "MCP Rewrite" +description: "Expose user-facing names for MCP tools, resources, and prompts while mapping them to different backend capability names." +canonical_url: https://wso2.com/api-platform/docs/ai-gateway/mcp-proxy/policies/mcp-rewrite/ +md_url: https://wso2.com/api-platform/docs/ai-gateway/mcp-proxy/policies/mcp-rewrite.md +tags: + - ai-gateway + - mcp + - policies +author: WSO2 API Platform Documentation Team +last_updated: 2026-06-16 +content_type: "reference" +--- + +# MCP Rewrite + +## Overview + +The MCP Rewrite policy enables API administrators to expose user-facing names for Model Context Protocol (MCP) tools, resources, and prompts while mapping them to different backend capability names. This policy supports three types of MCP capabilities: tools, resources, and prompts. For each capability type, you can define a list of user-facing capabilities with optional mappings to backend names, and optionally specify additional metadata fields to be returned in list responses. + +When a list is provided for a capability type, only the configured capabilities are included in list responses. Requests for unlisted capabilities are rejected with an appropriate error. The policy rewrites request payloads to use backend capability names when configured, and rewrites list responses to return user-facing values. + +## Features + +- **Tool Rewriting**: Define user-facing tool names and map them to backend tool names with custom schemas and descriptions. +- **Resource Rewriting**: Define user-facing resource identifiers and map them to backend resource identifiers with custom descriptions. +- **Prompt Rewriting**: Define user-facing prompt names and map them to backend prompt names with custom metadata. +- **Flexible Metadata**: Include additional fields (beyond `name`, `description`, `target`, etc.) in capability definitions for custom metadata in list responses. +- **Optional Mapping**: Omit the `target` field to expose capabilities as-is without mapping to a different backend name. + +## Configuration + +The MCP Rewrite policy uses a single-level configuration model where all parameters are configured per-MCP-API/route in the API definition YAML. + +### User Parameters (API Definition) + +These parameters are configured per MCP Proxy by the API developer: + +| Parameter | Type | Required | Description | +|-----------|------|----------|-------------| +| `tools` | array | No | List of tools to expose and optionally rewrite. Each entry must include `name` and `description`, and should include `inputSchema`. When provided (non-empty), only these tools are included in tools/list responses. | +| `tools[].name` | string | Yes | User-facing tool name exposed to clients (1-256 characters). | +| `tools[].description` | string | Yes | User-facing tool description returned in tools/list. | +| `tools[].inputSchema` | string | Yes | Tool input schema returned in tools/list. | +| `tools[].outputSchema` | string | No | Tool output schema returned in tools/list. | +| `tools[].target` | string | No | Backend tool name to use when forwarding requests. If omitted, the `name` is used. | +| `resources` | array | No | List of resources to expose and optionally rewrite. Each entry must include `name` and `uri`. When provided (non-empty), only these resources are included in resources/list responses. | +| `resources[].name` | string | Yes | User-facing resource identifier exposed to clients (1-1024 characters). | +| `resources[].uri` | string | Yes | User-facing resource URI returned in resources/list (1-2048 characters). | +| `resources[].description` | string | No | User-facing resource description returned in resources/list. | +| `resources[].target` | string | No | Backend resource identifier (URI) to use when forwarding requests. If omitted, the `uri` is used. | +| `prompts` | array | No | List of prompts to expose and optionally rewrite. Each entry must include `name`. When provided (non-empty), only these prompts are included in prompts/list responses. | +| `prompts[].name` | string | Yes | User-facing prompt name exposed to clients (1-256 characters). | +| `prompts[].description` | string | No | User-facing prompt description returned in prompts/list. | +| `prompts[].target` | string | No | Backend prompt name to use when forwarding requests. If omitted, the `name` is used. | + +> **Note**: Additional custom fields can be included in `tools`, `resources`, and `prompts` definitions and will be returned in the corresponding list responses. + +## MCP Proxy Definition Examples + +### Example 1: Basic Tool Rewriting + +Expose tools with different names than the backend: + +```yaml +apiVersion: gateway.api-platform.wso2.com/v1alpha1 +kind: Mcp +metadata: + name: mcp-server-api-v1.0 +spec: + displayName: mcp-server-api + version: v1.0 + context: /mcpserver + upstream: + url: https://mcp-backend:8080 + policies: + - name: mcp-rewrite + version: v0.1.0 + params: + tools: + - name: list-files + description: List files in a directory + inputSchema: '{"type": "object", "properties": {"path": {"type": "string"}}}' + target: backend_list_files + - name: read-file + description: Read file contents + inputSchema: '{"type": "object", "properties": {"path": {"type": "string"}}}' + target: backend_read_file + tools: + ... +``` + +### Example 2: Resource Rewriting with URI Mapping + +Expose resources with user-friendly URIs mapped to backend resources: + +```yaml +apiVersion: gateway.api-platform.wso2.com/v1alpha1 +kind: Mcp +metadata: + name: mcp-server-api-v1.0 +spec: + displayName: mcp-server-api + version: v1.0 + context: /mcpserver + upstream: + url: https://mcp-backend:8080 + policies: + - name: mcp-rewrite + version: v0.1.0 + params: + resources: + - name: user-docs + uri: file:///user-documentation + description: User documentation files + target: file:///internal/docs/users + - name: api-specs + uri: file:///api-specifications + description: API specification files + target: file:///internal/specs/api + resources: + ... +``` + +### Example 3: Prompt and Tool Rewriting Combined + +Rewrite prompts and tools with metadata: + +```yaml +apiVersion: gateway.api-platform.wso2.com/v1alpha1 +kind: Mcp +metadata: + name: mcp-server-api-v1.0 +spec: + displayName: mcp-server-api + version: v1.0 + context: /mcpserver + upstream: + url: https://mcp-backend:8080 + policies: + - name: mcp-rewrite + version: v0.1.0 + params: + tools: + - name: create-document + description: Create a new document + inputSchema: '{"type": "object", "properties": {"title": {"type": "string"}}}' + target: create_doc + prompts: + - name: summarize + description: Summarize content + target: summarize_content + category: text-processing + tools: + ... +``` + +## Use Cases + +1. **Semantic Naming**: Use user-friendly names in the API while keeping backend names internal or legacy. +2. **AI Readiness**: Redefine name, description, input schema, etc. in a way that is friendly to the AI agents. diff --git a/en/docs/ai-gateway/1.2.0/mcp-proxy/quick-start-guide.md b/en/docs/ai-gateway/1.2.0/mcp-proxy/quick-start-guide.md new file mode 100644 index 000000000..0f8742155 --- /dev/null +++ b/en/docs/ai-gateway/1.2.0/mcp-proxy/quick-start-guide.md @@ -0,0 +1,183 @@ +--- +title: "MCP Proxy Quick Start Guide" +description: "Run API Platform AI Gateway with Docker Compose, configure an MCP proxy, and route your first MCP traffic through the gateway." +canonical_url: https://wso2.com/api-platform/docs/ai-gateway/mcp-proxy/quick-start-guide/ +md_url: https://wso2.com/api-platform/docs/ai-gateway/mcp-proxy/quick-start-guide.md +tags: + - ai-gateway + - mcp + - quickstart +author: WSO2 API Platform Documentation Team +last_updated: 2026-08-05 +content_type: "quickstart" +--- + +## Quick Start + +!!! info "Watch the video walkthrough" + [Check out this quick start on YouTube](https://youtu.be/xt6RIax__dU?rel=0) or watch below. + + + +### Using Docker Compose (Recommended) + + +### Prerequisites + +A Docker-compatible container runtime such as: + +- Docker Desktop (Windows / macOS) +- Podman Desktop or Podman (Windows / macOS / Linux) +- Rancher Desktop (Windows / macOS) +- Colima (macOS) +- Docker Engine + Compose plugin (Linux) + +These examples use `docker compose`. If you use another Compose-compatible runtime, use the equivalent commands. + +Verify the commands for your runtime are available. For Docker: + +```bash +docker --version +docker compose version +``` + +```bash +# Download distribution. +wget https://github.com/wso2/api-platform/releases/download/ai-gateway/v1.2.0-rc2/wso2apip-ai-gateway-1.2.0-rc2.zip + +# Unzip the downloaded distribution. +unzip wso2apip-ai-gateway-1.2.0-rc2.zip + +cd wso2apip-ai-gateway-1.2.0/ + +# Run the one-time setup. This provisions the AES-256 at-rest encryption key, the router HTTPS +# listener certificate, api-platform.env, and the gateway-controller admin credentials. It prints +# the admin password once — copy it. +./scripts/setup.sh + +# Export the admin credentials so the management-API calls below can authenticate. +# The username defaults to "admin"; use the password setup.sh just printed. +export ADMIN_USERNAME=admin +export ADMIN_PASSWORD='' + +# Start the complete stack +docker compose -p ai-gateway up -d + +# Verify gateway controller admin endpoint is running +curl http://localhost:9094/api/admin/v1/health +``` + +!!! tip "Port 8080, 8443, 9090, or 9094 already taken?" + If the start command fails with a port binding error, identify what is already listening on the default ports: + + On macOS or Linux, run: + + ```bash + lsof -nP -iTCP:8080 -sTCP:LISTEN + lsof -nP -iTCP:8443 -sTCP:LISTEN + lsof -nP -iTCP:9090 -sTCP:LISTEN + lsof -nP -iTCP:9094 -sTCP:LISTEN + ``` + + On Windows PowerShell, run: + + ```powershell + Get-NetTCPConnection -State Listen -LocalPort 8080,8443,9090,9094 | Select-Object LocalAddress, LocalPort, OwningProcess + ``` + + Stop the conflicting service if you don't need it. If you need to keep it running, change the host-side value of the relevant `ports:` mapping in `docker-compose.yaml`. Then use the remapped host port in the verification and test commands on this page. + +!!! note "Running on Windows" + The commands above assume a Linux/macOS shell. On Windows, run the one-time setup with the PowerShell script instead — it takes the same flags and provisions the same files: + + ```powershell + powershell -ExecutionPolicy Bypass -File .\scripts\setup.ps1 + ``` + + Then set the admin credentials with `$env:ADMIN_USERNAME='admin'` and `$env:ADMIN_PASSWORD=''` in place of the `export` lines. + + The remaining `curl` commands on this page pipe their YAML payload in through a shell heredoc (`--data-binary @- <<'EOF'`), which PowerShell does not support. Either run them from Git Bash or WSL, or save the YAML between `EOF` markers to a file and post that file explicitly — note the `.exe`, since `curl` is an alias for `Invoke-WebRequest` in Windows PowerShell: + + ```powershell + curl.exe -X POST http://localhost:9090/api/management/v1/mcp-proxies ` + -H "Content-Type: application/yaml" ` + -u "${env:ADMIN_USERNAME}:${env:ADMIN_PASSWORD}" ` + --data-binary "@mcp-proxy.yaml" + ``` + +## Deploy an MCP proxy configuration + +Start the sample MCP server + +```bash +docker run -p 3001:3001 --name everything --network ai-gateway_gateway-network rakhitharr/mcp-everything:v3 +``` + +Run the following command to deploy the MCP proxy. + +```bash +curl -X POST http://localhost:9090/api/management/v1/mcp-proxies \ + -H "Content-Type: application/yaml" \ + -u "$ADMIN_USERNAME:$ADMIN_PASSWORD" \ + --data-binary @- <<'EOF' +apiVersion: gateway.api-platform.wso2.com/v1 +kind: Mcp +metadata: + name: everything-mcp-v1.0 + annotations: + "gateway.api-platform.wso2.com/project-id": "default" +spec: + displayName: Everything + version: v1.0 + context: /everything + specVersion: "2025-06-18" + upstream: + url: http://everything:3001 + tools: [] + resources: [] + prompts: [] +EOF +``` +To test MCP traffic routing through the gateway, add the following URL to your MCP client and connect to the server. + +``` +http://localhost:8080/everything/mcp +``` + +## View the MCP proxy in AI Workspace + +The gateway syncs the artifacts you deploy on it up to [AI Workspace](../../../next/ai-workspace/overview.md), the control plane for AI traffic across your organization. The `everything-mcp-v1.0` proxy you deployed above appears there without being re-declared, in the `default` project named in its `project-id` annotation. See [Manage Gateway-deployed AI artifacts in AI Workspace](../../../next/ai-workspace/sync-gateway-created-artifacts.md). + +## Stopping the Gateway + +Stop and remove the MCP backend first. + +```bash +docker stop everything +docker rm everything +``` + +When stopping the gateway, you have two options: + +### Option 1: Stop runtime, keep data (persisted proxies and configuration) + +```bash +docker compose -p ai-gateway down +``` + +This stops the containers but preserves the `controller-data` volume. When you restart with `docker compose -p ai-gateway up`, all your API configurations will be restored. + +### Option 2: Complete shutdown with data cleanup (fresh start) + +```bash +docker compose -p ai-gateway down -v +``` +This stops containers and removes the `controller-data` volume. Next startup will be a clean slate with no persisted proxies or configuration. diff --git a/en/docs/ai-gateway/1.2.0/observability/logging.md b/en/docs/ai-gateway/1.2.0/observability/logging.md new file mode 100644 index 000000000..8565c1a76 --- /dev/null +++ b/en/docs/ai-gateway/1.2.0/observability/logging.md @@ -0,0 +1,347 @@ +--- +title: "Gateway Logging" +description: "Configure centralized log collection for API Platform AI Gateway using Fluent Bit, OpenSearch, and alternative logging stacks." +canonical_url: https://wso2.com/api-platform/docs/ai-gateway/observability/logging/ +md_url: https://wso2.com/api-platform/docs/ai-gateway/observability/logging.md +tags: + - ai-gateway + - observability + - devops +author: WSO2 API Platform Documentation Team +last_updated: 2026-06-16 +content_type: "how-to" +--- + +# Gateway Logging + +This guide explains how to implement and configure logging for the API Platform Gateway components. + +## Overview + +The default logging services included in the Docker Compose configuration are **demonstration services** designed to showcase how you can observe component logs in a centralized setup. These services provide a reference implementation that you can use out-of-the-box for development, testing, or as a starting point for your production logging strategy. + +**Important**: You are free to choose any logging or observability strategy that suits your environment and requirements. The provided setup is just one of many possible configurations. + +## Logging Architecture + +The default logging stack consists of: + +- **Fluent Bit**: Lightweight log collector that reads Docker container logs and forwards them to OpenSearch +- **OpenSearch**: Stores and indexes log data for searchability and analysis +- **OpenSearch Dashboards**: Web interface for visualizing, exploring, and searching logs + +### How It Works + +1. Gateway components (gateway-controller, policy-engine, router) write structured JSON logs to stdout/stderr +2. Docker captures these logs and stores them in `/var/lib/docker/containers` +3. Fluent Bit tails these log files, parses them, and enriches them with metadata (component name, hostname) +4. Fluent Bit forwards processed logs to OpenSearch +5. Users can view and search logs through OpenSearch Dashboards + +## Enabling Logging Services + +### Gateway Components Already Log to Standard Output + +**No special configuration is required to enable logging in the gateway components.** All gateway components (gateway-controller, policy-engine, and router) follow the [12-factor app](https://12factor.net/logs) architecture principle for logging: + +- Components write all logs to **stdout (standard output)** and **stderr (standard error)** +- Logs are emitted as structured JSON for easy parsing +- No file-based logging or log management is built into the components + +This architecture approach allows you to utilize **any industry-standard logging stack** to collect logs from Docker container log files and view them in your preferred observability platform. The gateway components are completely decoupled from the logging infrastructure. + +### Demonstrated Logging Services + +The logging services included in the Docker Compose file (OpenSearch, OpenSearch Dashboards, and Fluent Bit) are provided as **demonstration services** to show one possible way to collect and visualize logs. You can use them as-is for development/testing, or replace them with your own logging solution. + +The gateway uses Docker Compose profiles to optionally enable these demonstration logging services. + +### Start Gateway with Demonstrated Logging Services + +To start the gateway with the demonstration logging services enabled: + +```bash +docker compose --profile logging up -d +``` + +This starts: +- Core gateway services (gateway-controller, policy-engine, router) - *which log to stdout/stderr* +- OpenSearch - *stores and indexes logs* +- OpenSearch Dashboards - *web UI for viewing logs* +- Fluent Bit - *collects logs from Docker and forwards to OpenSearch* + +### Start Gateway without Logging Services + +To run only the core gateway services without the demonstration logging stack: + +```bash +docker compose up +``` + +**Note:** The gateway components still log to stdout/stderr. You just won't have the centralized collection and visualization services running. You can still view logs using: + +```bash +docker logs gateway-controller +docker logs policy-engine +docker logs router +``` + +### Stop Logging Services + +To stop all services including the logging stack: + +```bash +docker compose --profile logging down +``` + +To completely remove logging data: + +```bash +docker compose --profile logging down -v +``` + +This removes the `opensearch-data` volume containing all stored logs. + +## Viewing Logs in OpenSearch Dashboards + +Once you've started the gateway with the logging profile, follow these steps to view component logs: + +### Step 1: Access OpenSearch Dashboards + +Open your browser and navigate to: +``` +http://localhost:5601 +``` + +### Step 2: Create an Index Pattern + +Before you can view logs, you need to create an index pattern: + +1. Click on the **hamburger menu (☰)** in the top-left corner +2. Navigate to **Management** → **Dashboard Management** +3. Under **Dashboard Management**, click **Index Patterns** +4. Click **Create index pattern** +5. Enter the index pattern: `gateway-logs-*` +6. Click **Next step** +7. Select **@timestamp** as the time field +8. Click **Create index pattern** + +### Step 3: Navigate to Discover + +To view and explore logs: + +1. Click the **hamburger menu (☰)** +2. Navigate to **OpenSearch Dashboards** → **Discover** +3. Select the `gateway-logs-*` index pattern from the dropdown in the top-left +4. Adjust the time range in the top-right corner if needed (default is last 15 minutes) + +### Step 4: Filter Logs by Component + +To view logs for a specific gateway component, use filters: + +#### View Policy Engine Logs + +1. Click **Add filter** (below the search bar) +2. **Field**: Select `component` +3. **Operator**: Select `is` +4. **Value**: Enter `policy-engine` +5. Click **Save** + +#### View Gateway Controller Logs + +1. Click **Add filter** +2. **Field**: `component` +3. **Operator**: `is` +4. **Value**: `gateway-controller` +5. Click **Save** + +#### View Router (Envoy) Logs + +1. Click **Add filter** +2. **Field**: `component` +3. **Operator**: `is` +4. **Value**: `router` +5. Click **Save** + +### Step 5: Search and Filter Logs + +You can refine your log search using: + +#### Free Text Search +Enter keywords in the search bar at the top: +``` +error +``` +``` +Weather-API +``` + +#### Filter by Log Level +1. Click **Add filter** +2. **Field**: `level` +3. **Operator**: `is` +4. **Value**: `error` (or `info`, `warn`, `debug`) + +#### Combine Multiple Filters +Add multiple filters to narrow down results. For example: +- Component: `policy-engine` +- Level: `error` +- Time range: Last 1 hour + +#### Example Search Queries + +Search for errors in the policy engine: +``` +component:policy-engine AND level:error +``` + +Search for specific API logs: +``` +component:gateway-controller AND message:*Weather-API* +``` + +Search for slow requests (if duration field exists): +``` +duration_ms:>1000 +``` + +### Step 6: Customize Log View + +- **Add/Remove Columns**: Click the **gear icon** next to the field list to select which fields to display +- **Sort**: Click column headers to sort by that field +- **Expand Logs**: Click the **>** arrow next to any log entry to see full details in JSON format +- **Save Search**: Click **Save** in the top menu to save your filters and queries for later use + +## Alternative Logging Stacks + +While the default setup uses OpenSearch and Fluent Bit, you can integrate with other logging platforms: + +### Elastic Stack (ELK) + +Replace OpenSearch with the Elastic Stack: + +```yaml +elasticsearch: + image: docker.elastic.co/elasticsearch/elasticsearch:8.11.0 + environment: + - discovery.type=single-node + - xpack.security.enabled=false + ports: + - "9200:9200" + networks: + - gateway-network + +kibana: + image: docker.elastic.co/kibana/kibana:8.11.0 + environment: + - ELASTICSEARCH_HOSTS=http://elasticsearch:9200 + ports: + - "5601:5601" + networks: + - gateway-network + depends_on: + - elasticsearch +``` + +Update Fluent Bit output: +```conf +[OUTPUT] + Name es + Match docker.* + Host elasticsearch + Port 9200 + Logstash_Format On + Logstash_Prefix gateway-logs +``` + +### Grafana Loki + +For a lightweight, Prometheus-inspired logging solution: + +```yaml +loki: + image: grafana/loki:latest + ports: + - "3100:3100" + command: -config.file=/etc/loki/local-config.yaml + networks: + - gateway-network + +promtail: + image: grafana/promtail:latest + volumes: + - /var/lib/docker/containers:/var/lib/docker/containers:ro + - ./observability/promtail/config.yaml:/etc/promtail/config.yaml:ro + command: -config.file=/etc/promtail/config.yaml + networks: + - gateway-network + +grafana: + image: grafana/grafana:latest + ports: + - "3000:3000" + networks: + - gateway-network + depends_on: + - loki +``` + +### Cloud-Native Solutions + +#### AWS CloudWatch + +Configure Fluent Bit to send logs to CloudWatch: + +```conf +[OUTPUT] + Name cloudwatch_logs + Match * + region us-east-1 + log_group_name /aws/gateway + log_stream_prefix gateway- + auto_create_group true +``` + +Add AWS credentials via environment variables or IAM roles. + +#### Datadog + +Use the Datadog Agent: + +```yaml +datadog: + image: datadog/agent:latest + environment: + - DD_API_KEY=${DD_API_KEY} + - DD_LOGS_ENABLED=true + - DD_LOGS_CONFIG_CONTAINER_COLLECT_ALL=true + - DD_AC_EXCLUDE=name:datadog-agent + volumes: + - /var/run/docker.sock:/var/run/docker.sock:ro + - /var/lib/docker/containers:/var/lib/docker/containers:ro + networks: + - gateway-network +``` + +#### Splunk + +Configure Fluent Bit to forward to Splunk HEC: + +```conf +[OUTPUT] + Name splunk + Match * + Host splunk.example.com + Port 8088 + Splunk_Token ${SPLUNK_HEC_TOKEN} + TLS On + TLS.Verify Off +``` + + +## Additional Resources + +- [Fluent Bit Documentation](https://docs.fluentbit.io/) +- [OpenSearch Documentation](https://opensearch.org/docs/latest/) +- [OpenSearch Dashboards User Guide](https://opensearch.org/docs/latest/dashboards/) +- [Docker Compose Profiles](https://docs.docker.com/compose/profiles/) diff --git a/en/docs/ai-gateway/1.2.0/observability/tracing.md b/en/docs/ai-gateway/1.2.0/observability/tracing.md new file mode 100644 index 000000000..9353ff60a --- /dev/null +++ b/en/docs/ai-gateway/1.2.0/observability/tracing.md @@ -0,0 +1,891 @@ +--- +title: "Gateway Tracing" +description: "Configure distributed tracing for API Platform AI Gateway using OpenTelemetry and Jaeger, with support for cloud-native tracing backends." +canonical_url: https://wso2.com/api-platform/docs/ai-gateway/observability/tracing/ +md_url: https://wso2.com/api-platform/docs/ai-gateway/observability/tracing.md +tags: + - ai-gateway + - observability + - devops +author: WSO2 API Platform Documentation Team +last_updated: 2026-06-16 +content_type: "how-to" +--- + +# Gateway Tracing + +This guide explains how to implement and configure distributed tracing for the API Platform Gateway components. + +## Overview + +The default tracing services included in the Docker Compose configuration are **demonstration services** designed to showcase how you can observe distributed traces across gateway components in a centralized setup. These services provide a reference implementation that you can use out-of-the-box for development, testing, or as a starting point for your production tracing strategy. + +**Important**: You are free to choose any tracing or observability strategy that suits your environment and requirements. The provided setup is just one of many possible configurations. + +## Tracing Architecture + +The default tracing stack consists of: + +- **OpenTelemetry (OTLP) Collector**: Receives, processes, and exports trace data from gateway components +- **Jaeger**: Stores and visualizes distributed traces with a web UI for trace exploration and analysis + +### How It Works + +1. Gateway components (gateway-controller, policy-engine, router) are configured to export traces via OTLP (OpenTelemetry Protocol) +2. Components send trace spans to the OpenTelemetry Collector via gRPC (port 4317) or HTTP (port 4318) +3. The OTLP Collector processes traces (batching, adding resource attributes, etc.) +4. The OTLP Collector forwards traces to Jaeger for storage and visualization +5. Users can view and analyze traces through the Jaeger UI + +### What is Distributed Tracing? + +Distributed tracing tracks a request as it flows through multiple components: +- **Trace**: Represents the entire journey of a request through the system +- **Span**: Represents a single operation within a trace (e.g., policy execution, upstream call) +- **Context Propagation**: Traces are correlated across components using trace IDs and span IDs in headers + +## Enabling Tracing + +### Configuration Required + +You need to enable tracing in the gateway configuration file and point it to your OTLP collector endpoint. + +The tracing configuration is located in `gateway/configs/config.toml`: + +#### Policy Engine Tracing Configuration + +```toml +[tracing] +enabled = true # Set to true to enable tracing +endpoint = "otel-collector:4317" # OTLP collector gRPC endpoint +service_version = "0.2.0" # Service version +batch_timeout = "1s" # Batch timeout for exporting spans +max_export_batch_size = 512 # Maximum spans per batch +sampling_rate = 1.0 # Sample rate (1.0 = 100%, 0.5 = 50%) +``` + +### Demonstrated Tracing Services + +The tracing services included in the Docker Compose file (Jaeger and OpenTelemetry Collector) are provided as **demonstration services** to show one possible way to collect and visualize traces. You can use them as-is for development/testing, or replace them with your own tracing solution. + +The gateway uses Docker Compose profiles to optionally enable these demonstration tracing services. + +### Start Gateway with Demonstrated Tracing Services + +To start the gateway with the demonstration tracing services enabled: + +```bash +docker compose --profile tracing up -d +``` + +This starts: +- Core gateway services (gateway-controller, policy-engine, router) - *which export traces to OTLP collector* +- OpenTelemetry Collector - *receives and processes traces* +- Jaeger - *stores and visualizes traces* + +### Start Gateway without Tracing Services + +To run only the core gateway services without the demonstration tracing stack: + +```bash +docker compose up +``` + +**Note:** If tracing is enabled in the configuration but the OTLP collector is not running, components will log warnings about failed trace exports. To completely disable tracing, set `enabled = false` in the configuration. + +### Stop Tracing Services + +To stop all services including the tracing stack: + +```bash +docker compose --profile tracing down +``` + +**Note:** Jaeger stores traces in memory by default. Stopping the service will lose all trace data. For persistent storage, configure Jaeger with a backend database (see Jaeger documentation). + +## Viewing Traces in Jaeger + +Once you've started the gateway with the tracing profile, follow these steps to view distributed traces: + +### Step 1: Access Jaeger UI + +Open your browser and navigate to: +``` +http://localhost:16686 +``` + +### Step 2: Search for Traces + +The Jaeger UI provides several ways to search for traces: + +1. **Select a Service** from the dropdown: + - `policy-engine` - View traces from the policy engine + - `router` - View traces from the Envoy router + +2. **Select an Operation** (optional): + - Choose "all" to see all operations + - Or select a specific operation (e.g., specific policy execution) + +3. **Adjust Lookback Time Range**: + - Default: Last 1 hour + - Options: 5m, 15m, 1h, 6h, 12h, 1d, 2d, Custom + +4. **Add Filters** (optional): + - **Tags**: Filter by specific tag values (e.g., `http.status_code=500`) + - **Min/Max Duration**: Filter by trace duration + - **Limit Results**: Control number of traces returned (default: 20) + +5. Click **Find Traces** + +### Step 3: Analyze Trace Details + +Click on any trace in the results to view detailed information: + +#### Trace Timeline +- **Visual timeline** showing all spans in the trace +- **Duration bars** showing relative time spent in each operation +- **Parent-child relationships** between spans +- **Color coding** by service + +#### Span Details +Click on any span to see: +- **Operation name**: What operation was performed +- **Duration**: How long it took +- **Tags**: Metadata about the operation (HTTP method, status code, etc.) +- **Logs**: Events logged during the span (errors, warnings, etc.) +- **Process**: Service name, version, and host information + +#### Common Use Cases + +**Finding Slow Requests:** +1. Set Min Duration filter (e.g., 1000ms) +2. Click Find Traces +3. Examine spans to identify bottlenecks + +**Debugging Errors:** +1. Filter by tag: `error=true` or `http.status_code=500` +2. Click on error traces +3. Examine span logs and tags for error details + +**Understanding Request Flow:** +1. Search for a specific trace ID (from logs or headers) +2. View the complete request path through all components +3. Identify which component handled which part of the request + +### Step 4: Trace Comparison + +You can compare multiple traces to identify patterns: +1. Select multiple traces using checkboxes +2. Click **Compare Traces** button +3. View side-by-side comparison of trace structure and timings + +### Step 5: Service Dependency Graph + +View how services interact: +1. Click **Dependencies** in the top navigation +2. Select time range +3. View graph showing service-to-service communication patterns + +## Configuration Options + +### Adjusting Sampling Rate + +To reduce trace volume in high-traffic environments, adjust the sampling rate: + +```toml +[tracing] +sampling_rate = 0.1 # Sample 10% of requests +``` + +Sampling strategies: +- `1.0` (100%): Sample all requests - recommended for development and low-traffic environments +- `0.5` (50%): Sample half of requests - moderate traffic +- `0.1` (10%): Sample 10% of requests - high traffic +- `0.01` (1%): Sample 1% of requests - very high traffic + +**Note:** Lower sampling rates reduce overhead but may miss important traces. + + +### Custom Service Names + +Customize service names for better identification: + +```toml +[policy_engine] +service_name = "policy-engine-prod-us-east-1" +``` + +### Batch Configuration + +Optimize batch settings for your environment: + +```toml +[tracing] +batch_timeout = "5s" # Wait up to 5s before exporting +max_export_batch_size = 1024 # Export up to 1024 spans per batch +``` + +**Lower timeout**: Faster trace visibility, more network overhead +**Higher timeout**: Better batching efficiency, slower trace visibility + +## Alternative Tracing Backends + +While the default setup uses Jaeger, the gateway components use OpenTelemetry and can export to any OTLP-compatible backend. + +### Moesif + +Moesif provides API analytics and monitoring with support for OpenTelemetry traces. It treats each HTTP request/response span as an API event for detailed analytics. + +**No additional Docker services required** - Moesif is a cloud-based SaaS platform. You only need to configure the OTLP Collector to export traces to Moesif's API. + +#### Configuration + +Update the OTLP Collector configuration (`gateway/observability/otel-collector/config.yaml`) to export to Moesif: + +```yaml +exporters: + # Export to Moesif + otlphttp: + endpoint: https://api.moesif.net/v1/traces + headers: + X-Moesif-Application-Id: 'your-moesif-application-id' + +service: + pipelines: + traces: + receivers: [otlp] + processors: [memory_limiter, batch, resource] + exporters: [otlphttp] # Send to Moesif +``` + +**Important Notes:** +- The endpoint uses HTTPS (not HTTP) +- Use the `otlphttp` exporter (not `otlp` which uses gRPC) +- The `X-Moesif-Application-Id` header is required for authentication + +#### Obtaining Your Moesif Application ID + +1. Sign up for a Moesif account at [moesif.com](https://www.moesif.com) +2. Log in to your Moesif dashboard +3. Navigate to **Settings** → **Installation** or **API Keys** +4. Locate the **Collector Application ID** field +5. Copy your unique Application ID + +#### Using Environment Variables + +For better security, use environment variables for the Application ID: + +```yaml +exporters: + otlphttp: + endpoint: https://api.moesif.net/v1/traces + headers: + X-Moesif-Application-Id: '${MOESIF_APPLICATION_ID}' +``` + +Update `docker-compose.yaml` to pass the environment variable: + +```yaml +otel-collector: + image: otel/opentelemetry-collector:latest + environment: + - MOESIF_APPLICATION_ID=${MOESIF_APPLICATION_ID} + # ... rest of configuration +``` + +Set the environment variable before starting: + +```bash +export MOESIF_APPLICATION_ID=your-moesif-application-id +docker compose --profile tracing up -d +``` + +#### Accessing Moesif Dashboard + +After configuring and starting the gateway: + +1. Navigate to [moesif.com](https://www.moesif.com) and log in +2. Go to **Events** → **Live Event Log** to see incoming API events +3. View API analytics, user behavior, and performance metrics +4. Use **Time Series** to analyze API usage trends +5. Set up **Alerts** for error rates, latency, or custom conditions + +#### Moesif Features + +- **API Analytics**: Request volume, response times, error rates +- **User Tracking**: Identify and track API users across requests +- **Error Analysis**: Detailed error tracking with request/response bodies +- **Behavioral Cohorts**: Group users by API usage patterns +- **Custom Dashboards**: Build visualizations for your specific KPIs +- **Alerting**: Get notified of anomalies or threshold breaches + +#### Sending to Both Jaeger and Moesif + +You can send traces to both Jaeger (for development) and Moesif (for analytics): + +```yaml +exporters: + # Local Jaeger for development + otlp/jaeger: + endpoint: jaeger:4317 + tls: + insecure: true + + # Moesif for analytics + otlphttp/moesif: + endpoint: https://api.moesif.net/v1/traces + headers: + X-Moesif-Application-Id: '${MOESIF_APPLICATION_ID}' + +service: + pipelines: + traces: + receivers: [otlp] + processors: [memory_limiter, batch, resource] + exporters: [otlp/jaeger, otlphttp/moesif] +``` + +### Zipkin + +Replace Jaeger with Zipkin: + +```yaml +zipkin: + image: openzipkin/zipkin:latest + ports: + - "9411:9411" + networks: + - gateway-network +``` + +Update OTLP Collector configuration to export to Zipkin: + +```yaml +exporters: + zipkin: + endpoint: http://zipkin:9411/api/v2/spans +``` + +Access Zipkin UI at `http://localhost:9411` + +### Grafana Tempo + +For a Prometheus-style tracing backend: + +```yaml +tempo: + image: grafana/tempo:latest + command: ["-config.file=/etc/tempo.yaml"] + volumes: + - ./observability/tempo/tempo.yaml:/etc/tempo.yaml + - tempo-data:/tmp/tempo + ports: + - "3200:3200" # Tempo HTTP + - "4317:4317" # OTLP gRPC + networks: + - gateway-network + +grafana: + image: grafana/grafana:latest + environment: + - GF_AUTH_ANONYMOUS_ENABLED=true + - GF_AUTH_ANONYMOUS_ORG_ROLE=Admin + ports: + - "3000:3000" + volumes: + - ./observability/grafana/datasources.yaml:/etc/grafana/provisioning/datasources/datasources.yaml + networks: + - gateway-network + depends_on: + - tempo +``` + +Configure gateway to send directly to Tempo: +```yaml +tracing: + endpoint: tempo:4317 +``` + +### Cloud-Native Tracing Solutions + +#### AWS X-Ray + +Configure OTLP Collector to export to AWS X-Ray: + +```yaml +exporters: + awsxray: + region: us-east-1 + no_verify_ssl: false +``` + +Or use the AWS Distro for OpenTelemetry (ADOT) Collector: + +```yaml +otel-collector: + image: public.ecr.aws/aws-observability/aws-otel-collector:latest + command: ["--config=/etc/otel-collector-config.yaml"] + environment: + - AWS_REGION=us-east-1 +``` + +#### Google Cloud Trace + +Configure OTLP Collector to export to Google Cloud: + +```yaml +exporters: + googlecloud: + project: your-gcp-project-id + use_insecure: false +``` + +#### Azure Monitor + +Use Azure Monitor exporter: + +```yaml +exporters: + azuremonitor: + instrumentation_key: "your-instrumentation-key" +``` + +#### Datadog APM + +Configure OTLP Collector to export to Datadog: + +```yaml +exporters: + datadog: + api: + key: ${DD_API_KEY} + site: datadoghq.com +``` + +Or use Datadog Agent directly: + +```yaml +datadog-agent: + image: datadog/agent:latest + environment: + - DD_API_KEY=${DD_API_KEY} + - DD_APM_ENABLED=true + - DD_APM_NON_LOCAL_TRAFFIC=true + - DD_OTLP_CONFIG_RECEIVER_PROTOCOLS_GRPC_ENDPOINT=0.0.0.0:4317 + ports: + - "4317:4317" + networks: + - gateway-network +``` + +Update gateway configuration: +```yaml +tracing: + endpoint: datadog-agent:4317 +``` + +#### New Relic + +Configure OTLP Collector to export to New Relic: + +```yaml +exporters: + otlphttp: + endpoint: https://otlp.nr-data.net:4317 + headers: + api-key: ${NEW_RELIC_LICENSE_KEY} +``` + +#### Honeycomb + +```yaml +exporters: + otlp: + endpoint: api.honeycomb.io:443 + headers: + x-honeycomb-team: ${HONEYCOMB_API_KEY} +``` + +#### Lightstep + +```yaml +exporters: + otlp: + endpoint: ingest.lightstep.com:443 + headers: + lightstep-access-token: ${LIGHTSTEP_ACCESS_TOKEN} +``` + +### Service Mesh Integration + +If using a service mesh like Istio or Linkerd: + +#### Istio + +Istio automatically generates traces for service-to-service communication. Configure gateway components to propagate trace context: + +```yaml +tracing: + enabled: true + endpoint: istio-telemetry.istio-system:4317 +``` + +#### Linkerd + +Linkerd integrates with Jaeger via OpenTelemetry: + +```yaml +tracing: + enabled: true + endpoint: linkerd-collector.linkerd:4317 +``` + +## Customizing OpenTelemetry Collector + +The OTLP Collector configuration is located at: +``` +gateway/observability/otel-collector/config.yaml +``` + +### Configuration Structure + +The configuration consists of three main sections: + +#### Receivers +Define how traces are received: + +```yaml +receivers: + otlp: + protocols: + grpc: + endpoint: 0.0.0.0:4317 + http: + endpoint: 0.0.0.0:4318 +``` + +#### Processors +Transform and enrich traces: + +```yaml +processors: + # Batch spans for efficiency + batch: + timeout: 1s + send_batch_size: 1024 + + # Add resource attributes + resource: + attributes: + - key: environment + value: production + action: upsert + - key: cluster + value: us-west-2 + action: upsert + + # Memory limiter to prevent OOM + memory_limiter: + check_interval: 1s + limit_mib: 512 + + # Sampling processor + probabilistic_sampler: + sampling_percentage: 10 # Sample 10% of traces +``` + +#### Exporters +Define where traces are sent: + +```yaml +exporters: + # Send to Jaeger + otlp: + endpoint: jaeger:4317 + tls: + insecure: true + + # Debug output to console + debug: + verbosity: detailed + sampling_initial: 5 + sampling_thereafter: 200 +``` + +#### Service Pipeline +Connect receivers, processors, and exporters: + +```yaml +service: + pipelines: + traces: + receivers: [otlp] + processors: [memory_limiter, batch, resource] + exporters: [otlp, debug] +``` + +### Example: Multi-Backend Export + +Send traces to multiple backends simultaneously: + +```yaml +exporters: + otlp/jaeger: + endpoint: jaeger:4317 + tls: + insecure: true + + otlp/tempo: + endpoint: tempo:4317 + tls: + insecure: true + + datadog: + api: + key: ${DD_API_KEY} + +service: + pipelines: + traces: + receivers: [otlp] + processors: [memory_limiter, batch] + exporters: [otlp/jaeger, otlp/tempo, datadog] +``` + +### Example: Tail-Based Sampling + +Keep all error traces but sample successful traces: + +```yaml +processors: + tail_sampling: + policies: + - name: error-traces + type: status_code + status_code: + status_codes: [ERROR] + - name: slow-traces + type: latency + latency: + threshold_ms: 1000 + - name: probabilistic + type: probabilistic + probabilistic: + sampling_percentage: 10 +``` + +## Trace Context Propagation + +The gateway components automatically propagate trace context using standard W3C Trace Context headers: + +- `traceparent`: Contains trace ID, span ID, and sampling decision +- `tracestate`: Contains vendor-specific trace information + +When making requests to the gateway, you can: + +1. **Let the gateway create a new trace** (default) +2. **Propagate your own trace context** by including trace headers: + +```bash +curl http://localhost:8080/weather/v1.0/us/seattle \ + -H "traceparent: 00-0af7651916cd43dd8448eb211c80319c-b7ad6b7169203331-01" +``` + +This allows you to trace requests across your entire system, including services before and after the gateway. + +## Best Practices + +### Development +- Use 100% sampling rate (`sampling_rate: 1.0`) +- Enable debug output in OTLP collector +- Use Jaeger for quick trace visualization +- Keep trace data for 1-7 days + +### Production +- **Use managed services** (Datadog, New Relic, etc.) to reduce operational overhead +- **Implement appropriate sampling** (1-10% depending on traffic volume) +- **Enable TLS** for OTLP connections +- **Set resource limits** on OTLP collector +- **Monitor collector health** and performance +- **Implement trace retention policies** based on compliance and storage costs +- **Use tail-based sampling** to keep important traces (errors, slow requests) + +### Security +- Enable TLS for trace transmission +- Sanitize sensitive data from trace attributes +- Implement proper access controls for trace viewing +- Regularly audit who accesses trace data +- Consider data residency requirements + +### Performance +- Use appropriate sampling rates to balance visibility and overhead +- Configure batch settings to optimize network usage +- Monitor gateway component overhead from tracing +- Use asynchronous trace export (default with OTLP) +- Consider using tail-based sampling for high-volume environments + +### Sampling Strategy + +Choose sampling based on traffic volume: + +| Traffic Volume | Sampling Rate | Use Case | +|---------------|---------------|----------| +| < 100 req/s | 100% (1.0) | Full visibility, low overhead | +| 100-1000 req/s | 10-50% (0.1-0.5) | Balanced visibility and cost | +| 1000-10000 req/s | 1-10% (0.01-0.1) | Cost-effective, statistical sampling | +| > 10000 req/s | 0.1-1% (0.001-0.01) | Minimal overhead, error sampling | + +**Note:** Always use 100% sampling for errors using tail-based sampling. + +## Troubleshooting + +### Traces Not Appearing in Jaeger + +**1. Verify tracing is enabled in configuration:** +```bash +cat gateway/configs/config.toml | grep -A5 "tracing" +``` + +Ensure `enabled = true`. + +**2. Check OTLP Collector is running:** +```bash +docker ps | grep otel-collector +``` + +**3. View OTLP Collector logs:** +```bash +docker logs otel-collector +``` + +Look for connection errors or export failures. + +**4. Check Jaeger is running:** +```bash +docker ps | grep jaeger +curl http://localhost:16686/ +``` + +**5. Verify network connectivity:** +```bash +docker exec policy-engine ping otel-collector +docker exec otel-collector ping jaeger +``` + +**6. Check gateway component logs for trace export errors:** +```bash +docker logs policy-engine | grep -i trace +docker logs gateway-controller | grep -i trace +``` + +### Traces Are Incomplete or Missing Spans + +**1. Check sampling rate** - ensure it's not too low +**2. Verify all components are configured** to export traces +**3. Check for trace context propagation issues** - ensure headers are preserved +**4. Look for timeout errors** in OTLP collector logs + +### High Trace Export Overhead + +**1. Reduce sampling rate:** +```toml +[tracing] +sampling_rate = 0.1 # Reduce from 1.0 to 0.1 +``` + +**2. Increase batch size:** +```toml +[tracing] +batch_timeout = "5s" +max_export_batch_size = 2048 +``` + +**3. Use tail-based sampling** in OTLP collector to sample only important traces + +### Traces Have Incorrect Timing + +- **Ensure system clocks are synchronized** across all containers (use NTP) +- **Check for clock skew** in trace timeline view +- **Verify trace context propagation** is working correctly + +### Cannot Access Jaeger UI + +**1. Verify Jaeger is running:** +```bash +docker ps | grep jaeger +``` + +**2. Check Jaeger logs:** +```bash +docker logs jaeger +``` + +**3. Ensure port 16686 is not blocked:** +```bash +curl http://localhost:16686/ +``` + +## Disabling Tracing + +To completely disable tracing: + +1. **Update configuration** in `gateway/configs/config.toml`: + +```toml +[policy_engine.tracing] +enabled = false + +[tracing] +enabled = false +``` + +2. **Restart gateway services:** + +```bash +docker compose restart gateway-controller policy-engine router +``` + +**Note:** The router (Envoy) tracing is controlled by the gateway-controller configuration and will be disabled when the configuration is updated. + +## Integration with Logging + +Traces and logs work together for comprehensive observability: + +### Correlating Traces and Logs + +1. **Trace ID in Logs**: Gateway components include trace IDs in log entries +2. **Find Trace from Log**: Copy trace ID from log entry and search in Jaeger +3. **Find Logs from Trace**: Copy trace ID from Jaeger and search in log viewer + +Example log entry with trace ID: +```json +{ + "level": "info", + "ts": "2025-12-19T10:30:45.456Z", + "msg": "Policy executed", + "trace_id": "0af7651916cd43dd8448eb211c80319c", + "span_id": "b7ad6b7169203331", + "policy": "modify-headers" +} +``` + +### Using Both Stacks + +Enable both logging and tracing profiles: + +```bash +docker compose --profile logging --profile tracing up -d +``` + +This provides complete observability: +- **Traces**: Request flow and performance +- **Logs**: Detailed event information and debugging + +## Additional Resources + +- [OpenTelemetry Documentation](https://opentelemetry.io/docs/) +- [Jaeger Documentation](https://www.jaegertracing.io/docs/) +- [OpenTelemetry Collector Documentation](https://opentelemetry.io/docs/collector/) +- [W3C Trace Context Specification](https://www.w3.org/TR/trace-context/) +- [Gateway Logging Guide](logging.md) +- [Docker Compose Profiles](https://docs.docker.com/compose/profiles/) diff --git a/en/docs/ai-gateway/1.2.0/overview.md b/en/docs/ai-gateway/1.2.0/overview.md new file mode 100644 index 000000000..da2199c23 --- /dev/null +++ b/en/docs/ai-gateway/1.2.0/overview.md @@ -0,0 +1,123 @@ +--- +title: "API Platform AI Gateway Overview" +description: "Manage and secure AI traffic with API Platform AI Gateway: LLM providers, LLM proxies, MCP proxies, and guardrails for LLM APIs and MCP servers." +canonical_url: https://wso2.com/api-platform/docs/ai-gateway/overview/ +md_url: https://wso2.com/api-platform/docs/ai-gateway/overview.md +tags: + - ai-gateway + - llm + - mcp +author: WSO2 API Platform Documentation Team +last_updated: 2026-07-13 +content_type: "concept" +--- + +# API Platform AI Gateway + +A gateway for managing and securing AI traffic, including Large Language Model (LLM) APIs and Model Context Protocol (MCP) servers. + +## Quick Start + +- [LLM Quick Start Guide](llm-proxy/quick-start-guide.md) - Set up the gateway, verify the controller admin health endpoint, and route traffic to LLM providers like OpenAI +- [MCP Quick Start Guide](mcp-proxy/quick-start-guide.md) - Set up the gateway, verify the controller admin health endpoint, and route traffic to MCP servers + +## Key Concepts + +### LLM Provider Template + +An LLM Provider Template defines the characteristics and behaviors specific to an AI service provider, such as OpenAI, Azure OpenAI, or other LLM platforms. It describes how the gateway should interpret and extract usage and operational metadata, including prompt, completion, total, and remaining token information, as well as request and response model metadata. + +Following templates are shipped out-of-the-box + +- OpenAI +- Azure OpenAI +- Anthropic +- AWS Bedrock +- Azure AI Foundry +- Gemini + +### LLM Provider + +An LLM Provider represents a connection to an AI backend service such as OpenAI, Azure OpenAI, or other LLM APIs. Platform administrators configure LLM Providers to define: + +- The LLM Provider Template +- The upstream LLM service URL +- Authentication credentials (API keys, tokens) +- Access control rules for which endpoints are exposed +- Budget control policies, such as token-based rate limiting +- Organization-wide policies such as guardrails + +Once configured, the LLM Provider allows traffic to flow through the gateway to the AI backend. + +### LLM Proxy + +An LLM Proxy allows developers to create custom API endpoints that consume an LLM Provider, while inheriting administrator-enforced access control, budgeting and organization-wide policies defined at the provider level. Each proxy gets its own URL context (e.g., `/assistant`) and can have its own policies applied. This enables: + +- Multiple AI applications to share a single LLM Provider +- A single OpenAI-compatible endpoint to route requests to multiple LLM providers. See [Multi-Provider Routing for LLM Proxies](llm-proxy/multi-provider-routing.md). +- Per-application policies such as prompt management and guardrails +- Separation between platform administration and application development + +### MCP Proxy + +An MCP Proxy routes Model Context Protocol traffic to MCP servers. MCP is a protocol that enables AI assistants to interact with external tools and data sources. With MCP Proxies, you can: + +- Expose MCP servers through a centralized gateway +- Apply authentication and access control to MCP traffic +- Manage multiple MCP servers from a single control plane + +### Streaming + +When an upstream service streams its response, the gateway relays it to the client chunk by chunk instead of buffering the whole response. This holds for LLM providers and LLM proxies, and needs no configuration. On MCP proxies, request bodies stream, but response bodies stay buffered. See [Real-time AI streaming](streaming-responses.md). + +## Default Ports + +| Port | Service | Description | +|------|---------|-------------| +| 8080 | Router | HTTP traffic | +| 8443 | Router | HTTPS traffic | +| 9090 | Gateway-Controller | REST API | +| 9094 | Gateway-Controller Admin | Health and admin endpoints | + +## Architecture + +``` + ┌─────────────────┐ + │ LLM Providers │ + │ (OpenAI, etc.) │ + └────────▲────────┘ + │ +┌──────────┐ ┌──────────────┐ │ +│ AI Apps │───▶│ AI Gateway │────┤ +└──────────┘ └──────────────┘ │ + │ + ┌────────▼────────┐ + │ MCP Servers │ + └─────────────────┘ +``` + +**How it works:** + +1. Administrators verify the Gateway-Controller admin health endpoint and configure LLM Providers and MCP Proxies via the Gateway-Controller API +2. Developers create LLM Proxies to build AI applications on top of available providers +3. The gateway routes traffic, applies policies, and manages authentication + +## AI Guardrails + +AI Guardrails allow you to enforce safety, content, and compliance policies on AI traffic flowing through the AI Gateway. They can be applied at the LLM Provider level (organization-wide), at the LLM Proxy level (per-application), or on MCP Proxies. + +The complete and up-to-date guardrail catalogue — with configuration references and examples — is maintained in the gateway-controllers repository: [gateway-controllers documentation](https://github.com/wso2/gateway-controllers/blob/main/docs/README.md) + +You can extend the AI Gateway with custom guardrail policies by building a custom gateway image using the `ap` CLI. See [Customizing the Gateway by Adding and Removing Policies](../../tools/cli/customizing-gateway-policies.md). + +## Documentation + +| Section | Description | +|---------|-------------| +| [LLM](llm-proxy/quick-start-guide.md) | LLM provider configuration, guardrails, prompt management, and semantic caching | +| [MCP](mcp-proxy/quick-start-guide.md) | MCP proxy setup and policies | +| [Real-time AI streaming](streaming-responses.md) | Streamed responses across providers and proxies, and how policies and analytics behave | +| [Observability](observability/logging.md) | Logging and tracing configuration | +| [Analytics](analytics/moesif-analytics.md) | Analytics integrations (Moesif) | +| [Policies and Guardrails](https://github.com/wso2/gateway-controllers/blob/main/docs/README.md) | Gateway policies and guardrails for AI traffic control | +| [REST APIs](ai-gateway-rest-api/authentication.md) | REST API authentication and usage | diff --git a/en/docs/ai-gateway/1.2.0/performance/ai-gateway-runtime-with-four-cpus.md b/en/docs/ai-gateway/1.2.0/performance/ai-gateway-runtime-with-four-cpus.md new file mode 100644 index 000000000..724677e52 --- /dev/null +++ b/en/docs/ai-gateway/1.2.0/performance/ai-gateway-runtime-with-four-cpus.md @@ -0,0 +1,75 @@ +--- +title: "AI Gateway Runtime with Four CPUs - Performance Results" +description: "View API Platform AI Gateway performance benchmark results with a four-CPU gateway runtime, including throughput, average response time, and percentile response times for Auth No Guardrails, PII Masking, and Advanced Guardrails scenarios." +canonical_url: https://wso2.com/api-platform/docs/ai-gateway/performance/ai-gateway-runtime-with-four-cpus/ +md_url: https://wso2.com/api-platform/docs/ai-gateway/performance/ai-gateway-runtime-with-four-cpus.md +tags: + - ai-gateway + - performance + - benchmarks + - gateway-runtime +author: WSO2 API Platform Documentation Team +last_updated: 2026-07-15 +content_type: "reference" +--- + +# AI Gateway runtime with four CPUs + +The table below displays the resource allocations for the AI Gateway components used in the performance tests. + +| Component | CPU | Memory | Router Concurrency | GOMAXPROCS | +| ------------------ | --- | ------ | ------------------ | ---------- | +| Gateway Controller | 1 | 2 GB | — | — | +| Gateway Runtime | 4 | 2 GB | 4 | 4 | + +## Throughput (requests/sec) vs. concurrent users + +The graph below shows how AI Gateway throughput changes as concurrent users increase for AI API Auth No Guardrails, AI API PII Masking, and AI API Advanced Guardrails. + +![Line chart of AI Gateway throughput in requests per second versus concurrent users at 100, 500, 800, and 1000 users, comparing AI API Auth No Guardrails, AI API PII Masking, and AI API Advanced Guardrails on a four-CPU gateway runtime](../../../assets/img/ai-gateway/performance/four-core-Throughput-vs-Concurrent-Users.svg){ width="900" } + +**Key observations:** + +- The four-CPU gateway runtime supports higher overall request rates than the two-CPU configuration under comparable load. +- The lower relative throughput observed in AI API PII Masking and AI API Advanced Guardrails scenarios is attributable to the additional request and response processing carried out by the gateway around the backend call. +- Throughput for each scenario rises from low concurrency and then levels off toward higher concurrent-user counts. + +## Average response time (ms) vs. concurrent users + +The graph below shows how average response time changes for the same AI API scenarios as concurrent users increase. The backend delay was configured to 10 ms for these tests. + +![Line chart of average response time in milliseconds versus concurrent users at 100, 500, 800, and 1000 users, comparing AI API Auth No Guardrails, AI API PII Masking, and AI API Advanced Guardrails on a four-CPU gateway runtime](../../../assets/img/ai-gateway/performance/four-core-Average-Response-Time-vs-Concurrent-Users.svg){ width="900" } + +**Key observations:** + +- The four-CPU configuration improves response times compared with the two-CPU results under comparable load. +- AI API PII Masking takes longer than Auth No Guardrails because the gateway processes and masks data in both requests and responses. + +## Response time percentiles vs. concurrent users + +The graphs below show the 90th, 95th, and 99th percentile response times at 10 ms backend delay. Percentile values indicate the response time below which that percentage of requests completed, for example, the 99th percentile is the response time exceeded by only 1% of requests. + +![Line chart of 90th, 95th, and 99th percentile response times in milliseconds versus concurrent users for AI API Auth No Guardrails on a four-CPU gateway runtime](../../../assets/img/ai-gateway/performance/4-core-Percentile-Response-Times-aiapi.svg){ width="900" } + +**Key observations:** + +- 90th, 95th, and 99th percentile response times increase as concurrent users grow. +- The four-CPU configuration yields lower percentile values at high concurrency than the two-CPU configuration. +- Percentile growth mainly reflects load on the gateway and the fixed backend delay, without content-level guardrail processing. + +![Line chart of 90th, 95th, and 99th percentile response times in milliseconds versus concurrent users for AI API PII Masking on a four-CPU gateway runtime](../../../assets/img/ai-gateway/performance/4-core-Percentile-Response-Times-pii-masking.svg){ width="900" } + +**Key observations:** + +- Percentile trends follow the same upward pattern as concurrent users increase across the test range. +- Compared with Auth No Guardrails, percentile values are higher at each concurrency level due to request and response masking. +- Compared with the two-CPU PII Masking results, the four-CPU configuration keeps percentiles lower under similar concurrency. + +![Line chart of 90th, 95th, and 99th percentile response times in milliseconds versus concurrent users for AI API Advanced Guardrails on a four-CPU gateway runtime](../../../assets/img/ai-gateway/performance/4-core-Percentile-Response-Times-advanced.svg){ width="900" } + +**Key observations:** + +- Percentile trends follow the same upward pattern as concurrent users increase across the test range. +- Compared with the two-CPU Advanced Guardrails results, the four-CPU configuration improves percentile response times at high concurrency. + +Test scenario results in CSV format are available [here](https://raw.githubusercontent.com/wso2/api-platform/refs/heads/main/gateway/perf/ai-gateway-1.1.0-perf-test-results/4-core-results-summary.csv). diff --git a/en/docs/ai-gateway/1.2.0/performance/ai-gateway-runtime-with-two-cpus.md b/en/docs/ai-gateway/1.2.0/performance/ai-gateway-runtime-with-two-cpus.md new file mode 100644 index 000000000..34da46d2b --- /dev/null +++ b/en/docs/ai-gateway/1.2.0/performance/ai-gateway-runtime-with-two-cpus.md @@ -0,0 +1,73 @@ +--- +title: "AI Gateway Runtime with Two CPUs - Performance Results" +description: "View API Platform AI Gateway performance benchmark results with a two-CPU gateway runtime, including throughput, average response time, and percentile response times for Auth No Guardrails, PII Masking, and Advanced Guardrails scenarios." +canonical_url: https://wso2.com/api-platform/docs/ai-gateway/performance/ai-gateway-runtime-with-two-cpus/ +md_url: https://wso2.com/api-platform/docs/ai-gateway/performance/ai-gateway-runtime-with-two-cpus.md +tags: + - ai-gateway + - performance + - benchmarks + - gateway-runtime +author: WSO2 API Platform Documentation Team +last_updated: 2026-07-15 +content_type: "reference" +--- + +# AI Gateway runtime with two CPUs + +The table below displays the resource allocations for the AI Gateway components used in the performance tests. + +| Component | CPU | Memory | Router Concurrency | GOMAXPROCS | +| ------------------ | --- | ------ | ------------------ | ---------- | +| Gateway Controller | 1 | 2 GB | — | — | +| Gateway Runtime | 2 | 2 GB | 2 | 2 | + +## Throughput (requests/sec) vs. concurrent users + +The graph below shows how AI Gateway throughput changes as concurrent users increase for AI API Auth No Guardrails, AI API PII Masking, and AI API Advanced Guardrails. + +![Line chart of AI Gateway throughput in requests per second versus concurrent users at 100, 500, and 1000 users, comparing AI API Auth No Guardrails, AI API PII Masking, and AI API Advanced Guardrails on a two-CPU gateway runtime](../../../assets/img/ai-gateway/performance/two-core-throughput-vs-concurrent-users.svg){ width="900" } + +**Key observations:** + +- Throughput for each scenario remains in a consistent range as concurrent users increase. +- AI API PII Masking and AI API Advanced Guardrails exhibit lower relative throughput because the gateway performs additional request and response processing, both before forwarding requests to the backend and after receiving responses. + +## Average response time (ms) vs. concurrent users + +The graph below shows how average response time changes for the same AI API scenarios as concurrent users increase. The backend delay was configured to 10 ms for these tests. + +![Line chart of average response time in milliseconds versus concurrent users at 100, 500, and 1000 users, comparing AI API Auth No Guardrails, AI API PII Masking, and AI API Advanced Guardrails on a two-CPU gateway runtime](../../../assets/img/ai-gateway/performance/two-core-average-response-time-vs-concurrent-users.svg){ width="900" } + +**Key observations:** + +- Average response time increases as concurrent users grow due to resource contention on the gateway runtime. +- AI API PII Masking takes longer than Auth No Guardrails because the gateway performs additional processing to identify and mask data in both requests and responses + +## Response time percentiles vs. concurrent users + +The graphs below show the 90th, 95th, and 99th percentile response times at 10 ms backend delay. Percentile values indicate the response time below which that percentage of requests completed, for example, the 99th percentile is the response time exceeded by only 1% of requests. + +![Line chart of 90th, 95th, and 99th percentile response times in milliseconds versus concurrent users for AI API Auth No Guardrails on a two-CPU gateway runtime](../../../assets/img/ai-gateway/performance/2-core-Percentile-Response-Times-ai-api.svg){ width="900" } + +**Key observations:** + +- 90th, 95th, and 99th percentile response times increase as concurrent users grow. +- Higher concurrency widens the spread between lower and upper percentiles. +- Because this scenario uses API key authentication without content guardrails, percentile growth mainly reflects gateway load and the backend delay. + +![Line chart of 90th, 95th, and 99th percentile response times in milliseconds versus concurrent users for AI API PII Masking on a two-CPU gateway runtime](../../../assets/img/ai-gateway/performance/2-core-Percentile-Response-AI-api-pii-masking.svg){ width="900" } + +**Key observations:** + +- Percentile trends follow the same upward pattern as concurrent users increase across the test range. +- Compared with Auth No Guardrails, percentile values sit higher at each concurrency level because of message inspection and masking. + +![Line chart of 90th, 95th, and 99th percentile response times in milliseconds versus concurrent users for AI API Advanced Guardrails on a two-CPU gateway runtime](../../../assets/img/ai-gateway/performance/2-core-Percentile-Response-Times-ai-api-advanced.svg){ width="900" } + +**Key observations:** + +- Percentile trends follow the same upward pattern as concurrent users increase across the test range. +- Compared with PII Masking alone, Advanced Guardrails shows higher relative percentiles due to the extra validation steps. + +Test scenario results in CSV format are available [here](https://raw.githubusercontent.com/wso2/api-platform/refs/heads/main/gateway/perf/ai-gateway-1.1.0-perf-test-results/2-core-results-summary.csv). diff --git a/en/docs/ai-gateway/1.2.0/performance/overview.md b/en/docs/ai-gateway/1.2.0/performance/overview.md new file mode 100644 index 000000000..481ee0c6d --- /dev/null +++ b/en/docs/ai-gateway/1.2.0/performance/overview.md @@ -0,0 +1,62 @@ +--- +title: "API Platform AI Gateway Performance" +description: "Review WSO2 API Platform AI Gateway performance test methodology, deployment architecture, metrics, and benchmark results for two-CPU and four-CPU gateway runtime configurations." +canonical_url: https://wso2.com/api-platform/docs/ai-gateway/performance/overview/ +md_url: https://wso2.com/api-platform/docs/ai-gateway/performance/overview.md +tags: + - ai-gateway + - performance + - benchmarks + - guardrails +author: WSO2 API Platform Documentation Team +last_updated: 2026-07-15 +content_type: "overview" +--- + +# API Platform AI Gateway Performance + +The performance of the WSO2 API Platform AI Gateway was evaluated using the following APIs, all of which invoke a simple Netty HTTP Echo Service as the backend. The Netty-based backend echoes each request after a configurable delay and with a configurable response size, simulating an LLM provider invocation. + +- **AI API Auth No Guardrails**: AI API invocation through the AI Gateway with API key authentication enabled and no guardrails applied. +- **AI API PII Masking**: AI API invocation through the AI Gateway with API key authentication and PII masking enabled for both requests and responses. +- **AI API Advanced Guardrails**: AI API invocation through the AI Gateway with API key authentication, request and response PII masking, and URL and JSON schema guardrails enabled. + +The performance tests were conducted with 100, 500, and 1000 concurrent users, where concurrent users represent multiple clients accessing the AI Gateway simultaneously. The backend response delay was configured to 10 ms, and the backend response size was set to 10 KiB. Each request used a payload size of 1 KiB. + +Apache JMeter was used as the test client. Each test scenario was executed for 15 minutes, including a 3-minute warm-up period. Performance metrics were calculated after excluding the warm-up period from the analysis. + +The following key metrics were used to evaluate AI Gateway performance: + +- **Throughput**: The number of requests processed by the AI Gateway per unit of time (requests per second). +- **Response time**: The end-to-end time taken to process a request. The complete response time distribution, including the 90th, 95th, and 99th percentile response times, was recorded and analyzed. + +## Deployment used for the test + +The diagram below shows the deployment architecture used for the performance tests documented here. + +![Performance test deployment architecture showing Apache JMeter client and servers, Netty HTTP backend, and WSO2 API Platform AI Gateway on separate EC2 instances connected in a test topology](../../../assets/img/ai-gateway/performance/deployment-used-for-test.png){ width="900" } + +| Component | EC2 Instance Type | vCPU | Memory (GiB) | +| ---------------------------- | ----------------- | :--: | :----------: | +| Apache JMeter Client | `c5.2xlarge` | 8 | 16 | +| Apache JMeter Servers | `c5.2xlarge` | 8 | 16 | +| Netty HTTP Backend | `c5.2xlarge` | 8 | 16 | +| WSO2 API Platform AI Gateway | `c5.4xlarge` | 16 | 32 | + +- The operating system is Amazon Linux 2023.11. +- Java version is Temurin JDK 21. + +## Performance test scripts + +All scripts used to analyze results are in the following repository. + +- [https://github.com/wso2/performance-common](https://github.com/wso2/performance-common). + +## Results + +The tests were executed using the user counts, payload size, and response size described above across two concurrency levels. For each concurrency level, the gateway runtime was allocated the corresponding CPU resources before deploying the AI Gateway test configurations. The table below summarizes the test scenarios covered in this document. + +| Test Scenario | CPU Allocation (Gateway Controller) | CPU Allocation (Gateway Runtime) | Router Concurrency | Test Results | +| ------------- | ----------------------------------- | -------------------------------- | ------------------ | ------------ | +| 1 | 1 | 2 | 2 | [AI Gateway runtime with two CPUs](./ai-gateway-runtime-with-two-cpus.md) | +| 2 | 1 | 4 | 4 | [AI Gateway runtime with four CPUs](./ai-gateway-runtime-with-four-cpus.md) | diff --git a/en/docs/ai-gateway/1.2.0/quick-start-guide.md b/en/docs/ai-gateway/1.2.0/quick-start-guide.md new file mode 100644 index 000000000..edb288f7a --- /dev/null +++ b/en/docs/ai-gateway/1.2.0/quick-start-guide.md @@ -0,0 +1,230 @@ +--- +title: "AI Gateway Quick Start Guide" +description: "Run API Platform AI Gateway with Docker Compose, deploy an LLM provider and an LLM proxy, route your first LLM request, and govern the gateway from AI Workspace." +canonical_url: https://wso2.com/api-platform/docs/ai-gateway/quick-start-guide/ +md_url: https://wso2.com/api-platform/docs/ai-gateway/quick-start-guide.md +tags: + - ai-gateway + - llm + - mcp + - quickstart + - docker +author: WSO2 API Platform Documentation Team +last_updated: 2026-08-04 +content_type: "quickstart" +--- + +# Quick Start Guide + +This guide takes you from a downloaded distribution to an LLM request routed through the API Platform AI Gateway, then shows you how to govern that gateway from [AI Workspace](../../next/ai-workspace/overview.md), the control plane for AI traffic. It's written for platform administrators and AI developers. + +!!! info "Watch the video walkthrough" + [Check out this quick start on YouTube](https://youtu.be/p5xBXZWt5GU?rel=0) or watch below. + + + +## Prerequisites + +A Docker-compatible container runtime such as: + +- Docker Desktop (Windows / macOS) +- Rancher Desktop (Windows / macOS) +- Colima (macOS) +- Docker Engine + Compose plugin (Linux) + +Ensure `docker` and `docker compose` commands are available. + +```bash +docker --version +docker compose version +``` + +To call an LLM through the gateway, you also need an OpenAI API key. + +## Start the gateway + +The commands below use version `1.2.0-rc`. Substitute the API Platform AI Gateway release version you want to run in the download URL, the archive name, and the directory name. + +```bash +# Download distribution. +wget https://github.com/wso2/api-platform/releases/download/ai-gateway/v1.2.0-rc/wso2apip-ai-gateway-1.2.0-rc.zip + +# Unzip the downloaded distribution. +unzip wso2apip-ai-gateway-1.2.0-rc.zip + +cd wso2apip-ai-gateway-1.2.0-rc/ + +# Run the one-time setup. This provisions the AES-256 at-rest encryption key, the router HTTPS +# listener certificate, api-platform.env, and the gateway-controller admin credentials. It prints +# the admin password once — copy it. +./scripts/setup.sh + +# Export the admin credentials so the management-API calls below can authenticate. +# The username defaults to "admin"; use the password setup.sh just printed. +export ADMIN_USERNAME=admin +export ADMIN_PASSWORD='' + +# Start the complete stack +docker compose up + +# Verify gateway controller admin endpoint is running +curl http://localhost:9094/api/admin/v1/health +``` + +!!! note "Running on Windows" + The commands above assume a Linux/macOS shell. On Windows, run the one-time setup with the PowerShell script instead — it takes the same flags and provisions the same files: + + ```powershell + powershell -ExecutionPolicy Bypass -File .\scripts\setup.ps1 + ``` + + Then set the admin credentials with `$env:ADMIN_USERNAME='admin'` and `$env:ADMIN_PASSWORD=''` in place of the `export` lines. + + The remaining `curl` commands on this page pipe their YAML payload in through a shell heredoc (`--data-binary @- <<'EOF'`), which PowerShell does not support. Either run them from Git Bash or WSL, or save the YAML between `EOF` markers to a file and post that file explicitly — note the `.exe`, since `curl` is an alias for `Invoke-WebRequest` in Windows PowerShell: + + ```powershell + curl.exe -X POST http://localhost:9090/api/management/v1/llm-providers ` + -H "Content-Type: application/yaml" ` + -u "${env:ADMIN_USERNAME}:${env:ADMIN_PASSWORD}" ` + --data-binary "@openai-provider.yaml" + ``` + +!!! tip "Customizing configuration" + The setup script (`setup.sh`, or `setup.ps1` on Windows) writes `api-platform.env`, which is loaded into the containers via Docker Compose `env_file`. To change the storage backend, connect to a control plane, or tune other settings, edit that file (or the `config.toml` interpolation tokens directly). See [Gateway Configuration and Environment Interpolation](./setup/configuration.md). + +## Deploy an OpenAI LLM provider configuration + +The API Platform Gateway includes first-class support for the OpenAI LLM provider. As a platform administrator, replace `` with your OpenAI API key and run the following command to deploy a sample OpenAI LLM provider. + +```bash +curl -X POST http://localhost:9090/api/management/v1/llm-providers \ + -H "Content-Type: application/yaml" \ + -u "$ADMIN_USERNAME:$ADMIN_PASSWORD" \ + --data-binary @- <<'EOF' +apiVersion: gateway.api-platform.wso2.com/v1 +kind: LlmProvider +metadata: + name: openai-provider +spec: + displayName: OpenAI Provider + version: v1.0 + template: openai + context: /openai/latest + upstream: + url: https://api.openai.com/v1 + auth: + type: api-key + header: Authorization + value: + accessControl: + mode: deny_all + exceptions: + - path: /chat/completions + methods: [POST] + - path: /models + methods: [GET] + - path: /models/{modelId} + methods: [GET] +EOF +``` + +To test LLM provider traffic routing through the gateway, invoke the following request. + +```bash +curl -X POST https://localhost:8443/openai/latest/chat/completions \ + -H "Content-Type: application/json" \ + -d '{ + "model": "gpt-4o-mini", + "messages": [ + { + "role": "user", + "content": "Hi" + } + ] + }' -k +``` + +## Deploy an LLM proxy configuration to consume an LLM provider + +The API Platform Gateway provides first-class support for configuring and deploying LLM proxies. As an AI developer, run the following command to deploy a sample LLM proxy that consumes the OpenAI LLM provider the platform administrator deployed above. + +```bash +curl -X POST http://localhost:9090/api/management/v1/llm-proxies \ + -H "Content-Type: application/yaml" \ + -u "$ADMIN_USERNAME:$ADMIN_PASSWORD" \ + --data-binary @- <<'EOF' +apiVersion: gateway.api-platform.wso2.com/v1 +kind: LlmProxy +metadata: + name: openai-assistant +spec: + displayName: OpenAI Assistant + version: v1.0 + context: /assistant + provider: + id: openai-provider + policies: [] +EOF +``` + +To test LLM proxy traffic routing through the gateway and consume the LLM provider, invoke the following request. + +```bash +curl -X POST "https://localhost:8443/assistant/chat/completions" \ + -H "Content-Type: application/json" \ + -d '{ + "model": "gpt-4o-mini", + "messages": [ + { + "role": "user", + "content": "Hi" + } + ] + }' -k +``` + +## Govern this gateway from AI Workspace + +The gateway you just started serves traffic on its own, and it doesn't have to run alone. [AI Workspace](../../next/ai-workspace/overview.md) is the control plane for AI traffic across your organization: one console for LLM providers, App LLM proxies, MCP proxies, policies such as guardrails and token-based rate limits, and the credentials behind them. Register this gateway with AI Workspace to govern every AI gateway you run from a single place, across every environment. + +Both directions work, and you can use them together: + +- **Top-down.** Configure an artifact in AI Workspace, apply policies to it, then deploy it to one or more gateways. +- **Bottom-up.** Keep deploying through the management API, the way this guide does. Every artifact you create on the gateway syncs up to AI Workspace automatically and appears there as a copy the gateway owns, so the OpenAI provider and the `openai-assistant` proxy you deployed above show up without being re-declared. To see what a synced artifact looks like, and what stays editable, see [Manage Gateway-deployed AI artifacts in AI Workspace](../../next/ai-workspace/sync-gateway-created-artifacts.md). + +The gateway keeps serving traffic either way. If AI Workspace is unreachable, the gateway carries on and the sync catches up once the connection is restored. + +## Stopping the gateway + +When stopping the gateway, you have two options: + +**Option 1: Stop runtime, keep data (persisted proxies and configuration)** + +```bash +docker compose down +``` + +This stops the containers but preserves the `controller-data` volume. When you restart with `docker compose up`, all your configurations are restored. + +**Option 2: Complete shutdown with data cleanup (fresh start)** + +```bash +docker compose down -v +``` + +This stops the containers and removes the `controller-data` volume. The next startup is a clean slate with no persisted templates or provider configuration. + +## Next steps + +- Route to more than one provider, with failover: [Multi-provider routing](./llm-proxy/multi-provider-routing.md) +- Add guardrails to a proxy, such as [PII masking](./llm-proxy/guardrails/pii-masking-regex.md) or a [JSON schema guardrail](./llm-proxy/guardrails/json-schema.md) +- Expose an MCP server through the gateway: [MCP proxy quick start guide](./mcp-proxy/quick-start-guide.md) +- Govern AI traffic across all your gateways from the control plane: [AI Workspace overview](../../next/ai-workspace/overview.md) diff --git a/en/docs/ai-gateway/1.2.0/resiliency/timeouts.md b/en/docs/ai-gateway/1.2.0/resiliency/timeouts.md new file mode 100644 index 000000000..25750345f --- /dev/null +++ b/en/docs/ai-gateway/1.2.0/resiliency/timeouts.md @@ -0,0 +1,283 @@ +--- +title: "Configure Gateway Timeouts" +description: "Configure gateway-level and API-level timeouts (connect, route, idle, and HTTP connection manager) in the API Platform AI Gateway to protect against slow or unreachable backends and slow clients." +canonical_url: https://wso2.com/api-platform/docs/ai-gateway/resiliency/timeouts/ +md_url: https://wso2.com/api-platform/docs/ai-gateway/resiliency/timeouts.md +tags: + - ai-gateway + - configuration + - networking + - resiliency +author: WSO2 API Platform Documentation Team +last_updated: 2026-06-30 +content_type: "how-to" +--- + +# Configuring Timeouts + +This guide explains how to configure **timeouts** for the API Platform Gateway so that requests to slow or unreachable backends, and slow downstream clients, fail within a predictable time instead of hanging indefinitely. + +Timeouts are configured at **two levels**: + +| Level | Where it is configured | Scope | Typical use | +|-------|------------------------|-------|-------------| +| **Gateway level** | Inside the gateway `config.toml` | Global defaults applied to **all** traffic on the gateway | Operator-wide baselines (connect timeout, default route timeout, downstream/HCM timeouts) | +| **API level** | The API definition — the `resilience` block and `upstreamDefinitions[].timeout.connect` | A single API, its routes, and its upstream clusters | Per-API/per-route overrides for backends that are faster or slower than the global default | + +The gateway level establishes the **defaults**. The API level **overrides** those defaults for a specific API. When an API does not specify a value, the gateway-level default applies. + +## Gateway-level timeouts (config.toml) + +These timeouts are global defaults that apply to every request handled by the gateway, regardless of which API serves it. You can use `gateway/configs/config-template.toml` as a reference when creating your own `config.toml`. + +There are two groups: + +- **Upstream timeouts** (`[router.upstream.timeouts]`) — govern the connection to, and the response from, the **backend**. +- **HTTP Connection Manager (HCM) timeouts** (`[router.http_listener.timeouts]`) — govern the **downstream** connection between the client and the gateway. + +### Upstream timeouts + +```toml +[router.upstream.timeouts] +route_timeout_ms = 60000 # Default per-route request→response timeout +route_idle_timeout_ms = 300000 # Default per-route stream idle timeout +connect_timeout_ms = 5000 # TCP connection establishment timeout +``` + +| Setting | Default | Maps to (Envoy) | Description | +|---------|---------|-----------------|---------| +| `connect_timeout_ms` | `5000` | Cluster `connect_timeout` | How long the gateway waits to **establish a TCP connection** to an upstream endpoint before failing the request. | +| `route_timeout_ms` | `60000` | `RouteAction.timeout` | Default maximum time for the **entire request→upstream-response** on a route. This is the default that an API's `resilience.timeout` overrides. | +| `route_idle_timeout_ms` | `300000` | `RouteAction.idle_timeout` | Default **per-route stream idle** timeout. This is the default that an API's `resilience.idleTimeout` overrides. | + +!!! note + All three values must be **positive** (greater than `0`). To disable a *route* timeout for a specific API, set its `resilience` value to `0s` (see [API-level timeouts](#api-level-timeouts)). + +### HTTP Connection Manager (downstream) timeouts + +These govern the connection between the **client and the gateway**. A value of `"0s"` disables the corresponding timeout. + +```toml +# HTTP Connection Manager (downstream) timeouts +[router.http_listener.timeouts] +request_timeout = "0s" # Max duration for the entire downstream request +request_headers_timeout = "0s" # Max duration to receive the complete request headers +stream_idle_timeout = "5m" # Idle timeout for a single HTTP stream/request +idle_timeout = "1h" # Idle timeout for the downstream connection +``` + +| Setting | Default | Maps to (Envoy) | Description | +|---------|---------|-----------------|---------| +| `request_timeout` | `0s` (disabled) | HCM `request_timeout` | Max time for the gateway to receive the **entire** downstream request. | +| `request_headers_timeout` | `0s` (disabled) | HCM `request_headers_timeout` | Max time to receive the **complete request headers**. | +| `stream_idle_timeout` | `5m` | HCM `stream_idle_timeout` | Idle timeout for a single HTTP **stream/request**. | +| `idle_timeout` | `1h` | HCM `common_http_protocol_options.idle_timeout` | Idle timeout for the downstream **connection**. | + +Durations use Go duration syntax with a single unit (for example `"30s"`, `"500ms"`, `"5m"`, `"1h"`). If a value is left unset, the Envoy default for that field applies. + +!!! warning + `idle_timeout` can also be disabled by setting it to zero explicitly. Disabling it has a high likelihood of yielding connection leaks (for example, due to lost TCP FIN packets). + +### Setting gateway-level timeouts in different deployments + +#### Standalone / local + +Configure the timeouts directly in `gateway/configs/config.toml` using the structure shown above. + +#### Kubernetes with Helm + +When deploying the gateway via the Helm chart, the same settings are controlled through Helm values under `gateway.config.router.*`. Example `values.yaml` snippet: + +```yaml +gateway: + config: + router: + upstream: + timeouts: + connect_timeout_ms: 5000 + route_timeout_ms: 60000 + route_idle_timeout_ms: 300000 + http_listener: + timeouts: + request_timeout: "0s" + request_headers_timeout: "0s" + stream_idle_timeout: "5m" + idle_timeout: "1h" +``` + +The chart renders these values into the generated `config.toml` used by the gateway controller. + +## API-level timeouts + +Two of the timeout layers are naturally per-API and can be tuned directly in the API definition, each through its own field: + +| Surface | Field(s) | Maps to (Envoy) | Overrides gateway default | +|---------|----------|-----------------|--------------------------| +| Connect timeout per upstream (`upstreamDefinitions[].timeout.connect`) | `connect` | Cluster `connect_timeout` | `connect_timeout_ms` | +| `resilience` block | `timeout`, `idleTimeout` | `RouteAction.timeout` / `idle_timeout` | `route_timeout_ms` / `route_idle_timeout_ms` | + +The HCM (downstream) timeouts have no per-API equivalent and remain gateway-level only. + +### Connect timeout per upstream (`upstreamDefinitions[].timeout.connect`) + +When an API routes through a named **upstream definition**, that definition can set its own `connect` timeout, which overrides the gateway-level `connect_timeout_ms` for the cluster built from it. Because the setting lives on the upstream definition, different upstreams used by the same API can have different connect timeouts. + +```yaml +apiVersion: gateway.api-platform.wso2.com/v1 +kind: LlmProvider +metadata: + name: openai-provider +spec: + displayName: OpenAI Provider + version: v1.0 + template: openai + context: /openai + upstreamDefinitions: + - name: openai-backend + timeout: + connect: 6000ms # overrides connect_timeout_ms for this cluster + upstreams: + - url: https://api.openai.com/v1 + upstream: + ref: openai-backend # this provider routes through the openai-backend cluster + accessControl: + mode: deny_all + exceptions: + - path: /chat/completions + methods: [POST] +``` + +The same shape applies to the `Mcp` kind — declare `upstreamDefinitions` at the spec level and point `upstream.ref` at the definition by name. + +- `connect` is a duration string (for example `6000ms`, `5s`). +- It governs only **TCP connection establishment** (plus the TLS handshake for HTTPS upstreams) — not the response wait, which is the route timeout below. +- An API that uses a direct `upstream.url` (instead of an `upstreamDefinitions` ref) uses the gateway-level `connect_timeout_ms`. + +!!! note + `upstreamDefinitions` — and therefore the per-upstream `connect` timeout — are supported only for the **`LlmProvider`** and **`Mcp`** kinds. **`LlmProxy`** does not connect to a backend directly (it loops back to its backing provider), so it has no upstream definition of its own. To bound connection establishment for a proxied call, configure the connect timeout on the backing `LlmProvider` to match your requirements. + +### Route timeouts (via `resilience` block) + +The `resilience` block lets an individual API override the gateway-level **route** timeouts for its own traffic. It maps to Envoy's `RouteAction` timeouts. + +!!! note + For all three kinds — **`LlmProvider`**, **`LlmProxy`**, and **`Mcp`** — the `resilience` block is supported at the **API level only**. There is no operation-level override; a single API-level block applies to every route generated for the API. + +It supports two fields: + +| Field | Maps to (Envoy) | Overrides gateway default | +|-------|-----------------|--------------------------| +| `timeout` | `RouteAction.timeout` | `route_timeout_ms` | +| `idleTimeout` | `RouteAction.idle_timeout` | `route_idle_timeout_ms` | + +!!! note "Duration format" + `timeout` and `idleTimeout` take single-unit duration strings (for example `30s`, `500ms`, `1.5m`). Use `0s` to disable a timeout, or omit the field to fall back to the gateway-level default. Compound (`1h30m`), negative (`-30s`), and unitless (`0`, `30`) values are rejected. + + +### Sample LLM Provider, LLM Proxy, and MCP: + +For `LlmProvider` and `LlmProxy`, the `resilience` block is supported at the **API level only**. LLM routes are generated by the gateway from the access-control configuration and policy attachments (rather than authored as explicit operations), so there is no operation-level override. A single API-level block applies to **all** routes generated for the LLM API. + +```yaml +apiVersion: gateway.api-platform.wso2.com/v1 +kind: LlmProvider +metadata: + name: openai-provider +spec: + displayName: OpenAI Provider + version: v1.0 + template: openai + context: /openai + upstream: + url: https://api.openai.com/v1 + accessControl: + mode: deny_all + exceptions: + - path: /chat/completions + methods: [POST] + # Applies to every route generated for this provider. + resilience: + timeout: 60s + idleTimeout: 30s +``` + +```yaml +apiVersion: gateway.api-platform.wso2.com/v1 +kind: LlmProxy +metadata: + name: openai-proxy +spec: + displayName: OpenAI Proxy + version: v1.0 + context: /openai-proxy + provider: + id: openai-provider + resilience: + timeout: 75s +``` + +!!! note "LLM Proxy double hop" + A request to a proxy traverses two routes — `client → proxy route → (loopback) → provider route → backend`. The **proxy's** `resilience.timeout` bounds the whole proxied call, while the backing **provider's** `resilience.timeout` bounds the provider→backend call. For a meaningful end-to-end budget, keep the proxy timeout greater than or equal to the provider timeout; the shorter of the two effectively wins. + +For `Mcp`, the `resilience` block applies to the traffic-forwarding routes generated for the proxy. Because MCP transports are long-lived streams, MCP treats the **route timeout** differently from the other kinds (see the note below). + +```yaml +apiVersion: gateway.api-platform.wso2.com/v1 +kind: Mcp +metadata: + name: everything-mcp +spec: + displayName: Everything MCP + version: v1.0 + context: /everything + upstream: + url: https://mcp.example.com + # Applies to the traffic-forwarding routes generated for this MCP proxy. + resilience: + idleTimeout: 30m +``` + +!!! note "MCP route timeout defaults to disabled" + MCP transports (SSE / streamable HTTP) are long-lived streams, so a finite route timeout would sever an otherwise healthy stream. For `Mcp`, the route `timeout` therefore defaults to **disabled (`0s`)** when the `resilience` block does not set it — unlike the other kinds, which fall back to the gateway-level `route_timeout_ms`. Liveness on a stalled stream is instead bounded by `idleTimeout`. You can still set `timeout` explicitly if you need a hard cap, but be aware it will cut long-lived streams. The per-upstream `connect` timeout still applies to MCP as usual. + +## Practical guidance + +### Connect timeout (`connect_timeout_ms`) + +- **Decrease** when backends are highly available and you want to fail fast on unhealthy or misconfigured targets, freeing resources quickly. +- **Increase** when backends sit behind slower networks/load balancers, or may experience cold starts or scaling events that briefly delay connection establishment. +- Avoid setting it too low — it may cause **false-positive timeouts** during short periods of backend slowness or network jitter. +- Set the **gateway default** for the common case, and override **per upstream** via `upstreamDefinitions[].timeout.connect` (see [Connect timeout per upstream](#connect-timeout-per-upstream-upstreamdefinitionstimeoutconnect)) when a specific backend needs a different connection budget. + +### Route timeout (`route_timeout_ms` / `resilience.timeout`) + +- Set the **gateway default** to a sane upper bound for typical backends, and use **API level** `resilience.timeout` for endpoints that are known to be faster (tighter budget) or legitimately slower (e.g. report generation, LLM completions). +- For streaming/long-lived responses (e.g. SSE from LLM backends), be mindful of `idleTimeout`: a long total `timeout` with a short `idleTimeout` can still cut a slow token stream. Leave `idleTimeout` generous (or unset) for streaming providers. + +### HCM/downstream timeouts + +- `request_headers_timeout` is a useful protection against slow-header (Slowloris-style) clients; enable it with a small value (for example `"5s"`) if your clients are expected to send headers promptly. +- `request_timeout` is disabled by default; enable it only if you want a hard cap on how long the gateway will spend receiving an entire downstream request. + +## Example scenarios + +### A slow-to-connect backend + +A backend may take a few seconds to accept new connections during peak load. Give connections more time than the default while still failing in a bounded time when the backend is unreachable: + +```toml +[router.upstream.timeouts] +connect_timeout_ms = 6000 +``` + +The gateway gives each upstream connection attempt up to 6 seconds; if the backend is down, requests fail after ~6 seconds (`503`) instead of hanging. + +### Disabling the route timeout for a long-running API + +```yaml +spec: + resilience: + timeout: 0s # no route (response) timeout for this API +``` + +Disabling the route timeout removes only the per-route **response deadline** — it does not make the request truly unbounded. The request is still subject to every other applicable timeout: the upstream `connect_timeout_ms`, the route `idleTimeout`, and the downstream HCM `stream_idle_timeout` / `idle_timeout`, any of which can still terminate a stalled stream or connection. In other words, behaviour is bounded by all applicable gateway- and API-level timeouts. Use with care. diff --git a/en/docs/ai-gateway/1.2.0/setup/configuration.md b/en/docs/ai-gateway/1.2.0/setup/configuration.md new file mode 100644 index 000000000..d74c0d490 --- /dev/null +++ b/en/docs/ai-gateway/1.2.0/setup/configuration.md @@ -0,0 +1,161 @@ +--- +title: "Gateway Configuration and Environment Interpolation" +description: "How the API Platform Gateway loads its config.toml, injects environment values through interpolation tokens, and bootstraps required keys and certificates with the setup script." +canonical_url: https://wso2.com/api-platform/docs/ai-gateway/setup/configuration/ +md_url: https://wso2.com/api-platform/docs/ai-gateway/setup/configuration.md +tags: + - api-gateway + - configuration + - interpolation +author: WSO2 API Platform Documentation Team +last_updated: 2026-07-22 +content_type: "reference" +--- + +# Gateway Configuration and Environment Interpolation + +The Gateway Controller and Policy Engine read their configuration from a TOML file (`config.toml`) layered over built-in defaults. This page explains how configuration is delivered, how environment values are injected through interpolation tokens, and how the one-time setup script provisions the keys and certificates the gateway requires. + +## How configuration is loaded + +Configuration is read from a TOML file mounted at `/etc/gateway-controller/config.toml` (controller) and `/etc/policy-engine/config.toml` (policy engine), layered over the built-in defaults. + +!!! important "Environment variables do not override config keys directly" + There is **no `APIP_GW_*` prefix that auto-maps environment variables onto config keys.** An environment value reaches a setting **only** through an explicit interpolation token written into the config file, resolved when the file is loaded. A field with no token always takes its literal TOML value (or the built-in default). + + This replaces the previous behavior, where an `APIP_GW_`-prefixed environment variable was mapped onto the matching config key and silently overrode the file. A bare `APIP_GW_*` variable with no matching token in `config.toml` is now ignored. + +## Interpolation tokens + +Two functions are available inside `config.toml`: + +{% raw %} + +| Token | Behavior | +|-------|----------| +| `{{ env "NAME" "default" }}` | Substitutes the value of environment variable `NAME`. If the variable is unset **or set-but-empty**, the `default` is used. | +| `{{ file "PATH" }}` | Reads a secret value from a mounted file at `PATH` — for injecting secrets from a mounted volume rather than an environment variable. | + +An example from the shipped `config.toml`: + +```toml +[controller.controlplane] +host = '{{ env "APIP_GW_CONTROLLER_CONTROLPLANE_HOST" "" }}' +token = '{{ env "APIP_GW_CONTROLLER_CONTROLPLANE_TOKEN" "" }}' + +[controller.storage] +type = '{{ env "APIP_GW_CONTROLLER_STORAGE_TYPE" "sqlite" }}' +``` + +{% endraw %} + +Every token in the shipped config carries a default, so an unset variable keeps the built-in value. + +!!! note "`APIP_GW_` is a naming convention, not a prefix override" + The shipped `config.toml` names its token arguments `APIP_GW_` (for example, `APIP_GW_CONTROLLER_STORAGE_TYPE` for `controller.storage.type`). This is purely a convention that makes the token argument readable — it is the literal string passed to the interpolation function, not a prefix that the loader interprets. Renaming the environment variable also requires editing the matching token in the config file. + +### Common tokens + +The shipped `config.toml` already carries interpolation tokens for the settings the sample deployments inject. The most common are: + +| Environment variable (token argument) | Config key | Description | +|----------------------------------------|------------|-------------| +| `APIP_GW_CONTROLLER_STORAGE_TYPE` | `controller.storage.type` | `sqlite`, `postgres`, `sqlserver`, or `memory` | +| `APIP_GW_CONTROLLER_STORAGE_SQLITE_PATH` | `controller.storage.sqlite.path` | Path to the SQLite database file | +| `APIP_GW_CONTROLLER_STORAGE_POSTGRES_PASSWORD` | `controller.storage.postgres.password` | PostgreSQL password | +| `APIP_GW_CONTROLLER_STORAGE_DATABASE_DSN` | `controller.storage.database.dsn` | SQL Server DSN (when storage type is `sqlserver`) | +| `APIP_GW_CONTROLLER_CONTROLPLANE_HOST` | `controller.controlplane.host` | Control plane endpoint (`host:port`) | +| `APIP_GW_CONTROLLER_CONTROLPLANE_TOKEN` | `controller.controlplane.token` | Control plane registration token | +| `APIP_GW_CONTROLLER_LOGGING_LEVEL` | `controller.logging.level` | `debug`, `info`, `warn`, `error` | + +For the complete list of tokens and every configurable option, refer to the [config template](https://github.com/wso2/api-platform/blob/main/gateway/configs/config-template.toml). + +!!! tip "Distinct from artifact templating" + The interpolation tokens described here apply to the gateway's **own `config.toml`**. API **artifacts** (RestApi, LLMProvider, and other resource files) support a separate, richer set of template functions (`env`, `required`, `redact`, `default`) applied to the artifact body. See [Artifact Templating](./artifact-templating.md) for that mechanism. + +## Delivering environment values + +For Docker Compose deployments, the sample composes deliver these values to the container from an `api-platform.env` file via the Compose `env_file:` directive: + +```yaml +services: + gateway-controller: + env_file: + - ./api-platform.env +``` + +`api-platform.env` is generated by the [setup script](#one-time-setup-with-the-setup-script). Add or edit variables there — for example, to connect to a control plane: + +```bash +# api-platform.env +APIP_GW_CONTROLLER_CONTROLPLANE_HOST=your-control-plane-host:9443 +APIP_GW_CONTROLLER_CONTROLPLANE_TOKEN= +``` + +For Kubernetes/Helm deployments, the chart renders `config.toml` into a ConfigMap and injects the runtime secrets (control plane token, database password) as interpolation tokens backed by Kubernetes Secrets — see [Security Hardening](../deployment/production-deployment/security-hardening.md) and [Database Configuration](../deployment/production-deployment/database-configuration.md). + +## No development / demo mode + +The gateway has **no development or demo mode** and **never auto-generates keys or certificates**. It fails closed with a descriptive error at startup if a required key or certificate is missing. Everything the gateway requires must be provisioned before it starts: + +- **AES-256 at-rest encryption key** — used to encrypt sensitive data at rest. Required; the controller will not start without it. +- **Router HTTPS listener certificate** — the TLS certificate/key for the router's HTTPS listener. +- **Admin credentials** — the gateway-controller management API basic-auth credential. There is no hardcoded `admin:admin`; the controller fails closed if basic auth is enabled with no credential. +- **`api-platform.env`** — runtime settings read directly by the gateway-runtime entrypoint (for example, `GATEWAY_CONTROLLER_HOST`, `LOG_LEVEL`). + +The setup script provisions all four. + +## One-time setup with the setup script + +The distribution ships `scripts/setup.sh` (and `scripts/setup.ps1`, its Windows PowerShell counterpart), which provisions everything a fresh gateway needs. Run it once before the first `docker compose up`: + +=== "Linux / macOS" + + ```bash + ./scripts/setup.sh + docker compose up + ``` + +=== "Windows (PowerShell)" + + ```powershell + powershell -ExecutionPolicy Bypass -File .\scripts\setup.ps1 + docker compose up + ``` + +The setup script provisions, idempotently: + +| Artifact | Location | Purpose | +|----------|----------|---------| +| Router listener certificate | `listener-certs/default-listener.{crt,key}` | Self-signed cert for the router HTTPS listener (SANs include `localhost`, `*.localhost`, `host.docker.internal`, `127.0.0.1`). | +| AES-256 encryption key | `aesgcm-keys/default-aesgcm256-v1.bin` | 32-byte at-rest encryption key, bind-mounted into the controller. | +| Admin credentials | `api-platform.env` | Gateway-controller REST/management API basic-auth credential (`APIP_GW_CONTROLLER_AUTH_BASIC_ADMIN_USERNAME` + bcrypt `..._PASSWORD_HASH`). | +| `api-platform.env` | `api-platform.env` | Runtime defaults loaded into the containers via `env_file`. | + +!!! note "Admin credentials" + The gateway-controller management API is protected by basic auth. You provide the plaintext + `ADMIN_USERNAME` (defaults to `admin`) and `ADMIN_PASSWORD` (used if set, otherwise prompted, + otherwise randomly generated) to the setup script; it writes + `APIP_GW_CONTROLLER_AUTH_BASIC_ADMIN_USERNAME` and the **bcrypt** `..._PASSWORD_HASH` into + `api-platform.env` (the tokens `config.toml` reads) and prints the plaintext password **once** — copy + it. For non-interactive use: `ADMIN_USERNAME=admin ADMIN_PASSWORD='…' ./scripts/setup.sh` (on Windows: + `$env:ADMIN_USERNAME='admin'; $env:ADMIN_PASSWORD='…'; powershell -ExecutionPolicy Bypass -File .\scripts\setup.ps1`). If those + tokens are unset when the controller starts with the shipped `config.toml`, it **refuses to start** + rather than running on an empty credential. + +The script is **idempotent** — existing files are kept, not overwritten. Flags: + +| Flag | Effect | +|------|--------| +| `--force` | Regenerate the certificate and encryption key (rotating them), rewrite `api-platform.env`, and re-provision the admin credentials (rotating the password). | +| `--certs-only` | Generate only the listener TLS certificate; skip the encryption key, admin credentials, and `api-platform.env`. | + +!!! warning "Rotating the encryption key" + Running the setup script with `--force` regenerates the AES-256 encryption key. Data encrypted with the previous key becomes unreadable. Only rotate the key deliberately. + +!!! note "Control plane connection is not configured by the script" + Connecting to a control plane is optional and is not configured by the setup script. To connect, add `APIP_GW_CONTROLLER_CONTROLPLANE_HOST` and `APIP_GW_CONTROLLER_CONTROLPLANE_TOKEN` to `api-platform.env` (both default to empty, which runs the gateway in standalone mode). + +--- + +[← Artifact Templating](./artifact-templating.md)  |  [Setting Up the Database →](./database-setup.md) diff --git a/en/docs/ai-gateway/1.2.0/setup/database-setup.md b/en/docs/ai-gateway/1.2.0/setup/database-setup.md new file mode 100644 index 000000000..e9002a692 --- /dev/null +++ b/en/docs/ai-gateway/1.2.0/setup/database-setup.md @@ -0,0 +1,335 @@ +--- +title: "Setting Up the Database" +description: "Create the database and apply the Gateway Controller schema for PostgreSQL or SQL Server before starting the API Platform AI Gateway." +canonical_url: https://wso2.com/api-platform/docs/ai-gateway/setup/database-setup/ +md_url: https://wso2.com/api-platform/docs/ai-gateway/setup/database-setup.md +tags: + - ai-gateway + - configuration + - postgresql + - sqlserver + - devops +author: WSO2 API Platform Documentation Team +last_updated: 2026-07-30 +content_type: "how-to" +--- + +# Setting Up the Database + +The Gateway Controller persists API configurations, subscriptions, applications, keys, and other metadata in a database. Three storage backends are supported, selected through `[controller.storage].type`: + +| `type` | Description | Schema provisioning | +|--------|-------------|---------------------| +| `sqlite` (default) | Embedded, file-based database (`./data/gateway.db`). Single replica only. | Created and migrated automatically on startup | +| `postgres` | External PostgreSQL. Required for multi-replica, high-availability deployments. | Must be provisioned before the controller starts | +| `sqlserver` | External Microsoft SQL Server. Required for multi-replica, high-availability deployments. | Must be provisioned before the controller starts | + +If you are using `sqlite`, there is nothing to do — the controller creates the database file itself on first start, and the rest of this page does not apply. +For **PostgreSQL** and **SQL Server**, the Gateway Controller connects to the database you point it at but does not run schema DDL against it. + +## Before You Begin + +- A running PostgreSQL or SQL Server instance that is reachable from every Gateway Controller replica. +- An administrative account on that instance that can create databases, logins, and tables. +- A database client on the machine you run the provisioning from — `psql` for PostgreSQL, `sqlcmd` for SQL Server. + +## Get the Schema Scripts + +The scripts ship inside the gateway distribution: + +```text +wso2apip-ai-gateway-/ +└── resources/ + └── gateway-controller/ + └── db-scripts/ + ├── gateway-controller-db.postgres.sql + └── gateway-controller-db.sqlserver.sql +``` + +If you are deploying from container images or Helm rather than the distribution zip, download them from the repository instead: + +- [gateway-controller-db.postgres.sql](https://github.com/wso2/api-platform/blob/main/gateway/gateway-controller/pkg/storage/gateway-controller-db.postgres.sql) +- [gateway-controller-db.sqlserver.sql](https://github.com/wso2/api-platform/blob/main/gateway/gateway-controller/pkg/storage/gateway-controller-db.sqlserver.sql) + +!!! important + Always use the scripts that ship with the gateway version you are deploying. Applying a script from a different release can leave the schema out of step with what the controller expects. + +## Step 1 - Create the Database and User + +Create an empty database and a dedicated account for the gateway. +(The schema should be applied by an account with DDL privileges.) + +=== "PostgreSQL" + + Connect as an administrator: + + ```bash + psql "host= port=5432 dbname=postgres user= sslmode=require" + ``` + + Create the database and a login for the gateway. + + ```sql + CREATE DATABASE gateway_controller; + CREATE USER gateway WITH PASSWORD 'your-db-password'; + ``` + +=== "SQL Server" + + Connect as an administrator: + + ```bash + sqlcmd -S ,1433 -U -P '' + ``` + + Create the database, login, and user. + + ```sql + CREATE DATABASE gateway_controller; + GO + CREATE LOGIN gateway WITH PASSWORD = 'your-db-password'; + GO + USE gateway_controller; + GO + CREATE USER gateway FOR LOGIN gateway; + GO + ``` + +## Step 2 - Apply the Schema + +Run the script for your database against the database you just created. + +=== "PostgreSQL" + + ```bash + psql "host= port=5432 dbname=gateway_controller user= sslmode=require" \ + -v ON_ERROR_STOP=1 \ + -f resources/gateway-controller/db-scripts/gateway-controller-db.postgres.sql + ``` + + `ON_ERROR_STOP=1` makes `psql` abort and return a non-zero exit code on the first failing statement, instead of continuing and leaving a partially created schema. + +=== "SQL Server" + + ```bash + sqlcmd -S ,1433 -d gateway_controller \ + -U -P '' -b \ + -i resources/gateway-controller/db-scripts/gateway-controller-db.sqlserver.sql + ``` + + `-b` makes `sqlcmd` exit with an error code if any statement in the batch fails. + + !!! tip + `sqlcmd` v18 and later negotiate an encrypted connection by default and reject certificates they cannot validate. For production, use `-N` with a properly trusted certificate so the connection is both encrypted and verified. Only add `-C` if you must connect to a server with a self-signed certificate — it disables certificate validation entirely, so treat it as a controlled, limited-use exception rather than a default troubleshooting flag. + +The scripts are idempotent — every object is guarded (`CREATE TABLE IF NOT EXISTS` on PostgreSQL, `IF OBJECT_ID(...) IS NULL` on SQL Server) — so re-running them is safe and creates only what is missing. + +## Step 3 - Apply the Event Gateway Schema (Event Gateway Only) + +The Event Gateway stores WebSub and WebBroker artifacts in the same database, in three tables the core script does not define: `websub_apis`, `webbroker_apis`, and `webhook_secrets`. Like the core schema, these are auto-created only for `sqlite`; for external databases you must apply them yourself. + +Apply the matching supplemental script after Step 2: + +- [eventgateway-db.postgres.sql](https://github.com/wso2/api-platform/blob/main/event-gateway/gateway-controller/pkg/dbschema/eventgateway-db.postgres.sql) +- [eventgateway-db.sqlserver.sql](https://github.com/wso2/api-platform/blob/main/event-gateway/gateway-controller/pkg/dbschema/eventgateway-db.sqlserver.sql) + +=== "PostgreSQL" + + ```bash + psql "host= port=5432 dbname=gateway_controller user= sslmode=require" \ + -v ON_ERROR_STOP=1 -f eventgateway-db.postgres.sql + ``` + +=== "SQL Server" + + ```bash + sqlcmd -S ,1433 -d gateway_controller \ + -U -P '' -b \ + -i eventgateway-db.sqlserver.sql + ``` + +Skip this step if you are not running the Event Gateway. + +## Step 4 - Grant Gateway Access + +With the tables in place give `gateway` the privileges the controller needs at runtime: `SELECT`, `INSERT`, `UPDATE`, and `DELETE`. + +=== "PostgreSQL" + + ```bash + psql "host= port=5432 dbname=gateway_controller user= sslmode=require" + ``` + + ```sql + GRANT CONNECT ON DATABASE gateway_controller TO gateway; + GRANT USAGE ON SCHEMA public TO gateway; + GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO gateway; + ``` + +=== "SQL Server" + + ```bash + sqlcmd -S ,1433 -d gateway_controller -U -P '' + ``` + + ```sql + ALTER ROLE db_datareader ADD MEMBER gateway; + ALTER ROLE db_datawriter ADD MEMBER gateway; + GO + ``` + +## Step 5 - Verify the Schema + +Confirm the tables exist before starting the gateway. + +=== "PostgreSQL" + + ```bash + psql "host= port=5432 dbname=gateway_controller user= sslmode=require" \ + -c "SELECT table_name FROM information_schema.tables WHERE table_schema = 'public' ORDER BY table_name;" + ``` + +=== "SQL Server" + + ```bash + sqlcmd -S ,1433 -d gateway_controller \ + -U -P '' \ + -Q "SELECT name FROM sys.tables ORDER BY name;" + ``` + +The core schema creates 15 tables: + +```text +api_keys application_api_keys applications +artifacts certificates events +gateway_states llm_providers llm_provider_templates +llm_proxies mcp_proxies rest_apis +secrets subscription_plans subscriptions +``` + +If you also applied the Event Gateway script, `websub_apis`, `webbroker_apis`, and `webhook_secrets` are present as well. + +## Step 6 - Point the Gateway Controller at the Database + +With the schema in place, configure the connection in `configs/config.toml`. + +=== "PostgreSQL" + + ```toml + [controller.storage] + type = "postgres" + + [controller.storage.postgres] + host = "" + port = 5432 + database = "gateway_controller" + user = "gateway" + password = '{% raw %}{{ env "APIP_GW_CONTROLLER_STORAGE_POSTGRES_PASSWORD" "" }}{% endraw %}' + sslmode = "require" # disable, require, verify-ca, verify-full + ``` + +=== "SQL Server" + + SQL Server uses the unified `[controller.storage.database]` block. TLS behavior is controlled by `options` rather than PostgreSQL's `sslmode`. Two forms are supported — if `dsn` is set, the discrete fields below it are ignored. + + The shipped Compose files use a single `dsn`, so no password is ever written into the file: + + ```toml + [controller.storage] + type = "sqlserver" + + [controller.storage.database] + driver = "sqlserver" + dsn = '{% raw %}{{ env "APIP_GW_CONTROLLER_STORAGE_DATABASE_DSN" "" }}{% endraw %}' + + [controller.storage.database.options] + encrypt = "true" # disable, false, true, strict + trust_server_certificate = "false" + ``` + + Discrete fields, the same shape as PostgreSQL's, are also supported — useful outside the reference Compose setup, e.g. under Kubernetes/Helm: + + ```toml + [controller.storage] + type = "sqlserver" + + [controller.storage.database] + driver = "sqlserver" + host = "" + port = 1433 + database = "gateway_controller" + user = "gateway" + password = '{% raw %}{{ env "APIP_GW_CONTROLLER_STORAGE_DATABASE_PASSWORD" "" }}{% endraw %}' + + [controller.storage.database.options] + encrypt = "true" # disable, false, true, strict + trust_server_certificate = "false" + ``` + + Unlike `dsn` and PostgreSQL's fields, these discrete SQL Server fields don't ship with an interpolation token by default — add one following the pattern below rather than writing the password literally. + +The {% raw %}`{{ env "..." "" }}`{% endraw %} form is the interpolation token already used in the shipped `config.toml` — it reads the value from an environment variable at load time instead of storing it in the file. Set the actual values in `api-platform.env`: + +```bash +# api-platform.env +APIP_GW_CONTROLLER_STORAGE_TYPE=postgres +APIP_GW_CONTROLLER_STORAGE_POSTGRES_HOST= +APIP_GW_CONTROLLER_STORAGE_POSTGRES_DATABASE=gateway_controller +APIP_GW_CONTROLLER_STORAGE_POSTGRES_USER=gateway +APIP_GW_CONTROLLER_STORAGE_POSTGRES_PASSWORD=your-db-password +``` + +For SQL Server, the shipped Compose files supply the whole connection string through `APIP_GW_CONTROLLER_STORAGE_DATABASE_DSN`. See [Gateway Configuration and Environment Interpolation](./configuration.md) for how interpolation works. For the full list of storage configuration options for both databases, refer to the [config template](https://github.com/wso2/api-platform/blob/main/gateway/configs/config-template.toml). + +Start the gateway: + +```bash +docker compose up +``` + +On startup the controller logs that it connected to the external database and that schema auto-apply was skipped. That message is expected — it confirms the controller is relying on the schema you provisioned. + +## Kubernetes Deployments + +The Helm charts do not include a bootstrap job that provisions the schema, so the same steps apply: create the database and run the scripts before `helm install`. Run them from any host with network access to the database — a CI job, a bastion host, or a temporary pod in the cluster: + +```bash +kubectl run psql-client --rm -it --restart=Never \ + --namespace \ + --image=postgres:16 -- \ + psql "host= port=5432 dbname=gateway_controller user= sslmode=require" +``` + +## Redis for Distributed Rate Limiting (Optional) + +To enable distributed rate limiting across multiple Gateway Runtime instances, configure the rate limiting policy to use Redis as the backend: + +```toml +[policy_configurations.ratelimit_v1] +algorithm = "fixed-window" +backend = "redis" + +[policy_configurations.ratelimit_v1.redis] +host = "redis.example.com" +port = 6379 +password = '{% raw %}{{ env "APIP_GW_RATELIMIT_REDIS_PASSWORD" "" }}{% endraw %}' +``` + +`config.toml` is interpolated the same way everywhere in the file, so the {% raw %}`{{ env "..." "" }}`{% endraw %} token above works here too — see [Gateway Configuration and Environment Interpolation](./configuration.md) rather than writing the password literally. + +For the full list of Redis configuration options, refer to the [Advanced Rate Limiting documentation](https://wso2.com/api-platform/policy-hub/policies/advanced-ratelimit). + +## Troubleshooting + +| Symptom | Cause | Fix | +|---------|-------|-----| +| PostgreSQL: `ERROR: relation "artifacts" does not exist` | The schema was never applied, or was applied to a different database | Re-run Step 2 against the database named in `[controller.storage.postgres].database` | +| SQL Server: `Invalid object name 'dbo.artifacts'` | Same as above | Re-run Step 2 against the database named in `[controller.storage.database].database` | +| SQL Server: `Msg 1934 ... CREATE INDEX failed ... 'QUOTED_IDENTIFIER'` | The script is from a release before the `SET` options were added | Use the script shipped with your gateway version | +| `permission denied for table ...` at runtime | Step 4 was skipped, or was run before Step 2/3 finished creating the tables it grants access to | Run [Step 4 - Grant Gateway Access](#step-4-grant-gateway-access) | +| Event Gateway fails on `websub_apis` / `webbroker_apis` / `webhook_secrets` | The supplemental Event Gateway script was not applied | Run Step 3, then re-run Step 4 so `gateway` gets access to the new tables | +| Controller connects but logs that schema auto-apply was skipped | Expected behavior for external databases | No action needed | + +--- + +[← Configuration & Interpolation](./configuration.md) diff --git a/en/docs/ai-gateway/1.2.0/streaming-responses.md b/en/docs/ai-gateway/1.2.0/streaming-responses.md new file mode 100644 index 000000000..6dea68a96 --- /dev/null +++ b/en/docs/ai-gateway/1.2.0/streaming-responses.md @@ -0,0 +1,103 @@ +--- +title: "Real-Time AI Streaming" +description: "Stream responses through API Platform AI Gateway chunk by chunk across LLM providers, LLM proxies, and MCP proxies, and understand how policies, analytics, and token usage behave." +canonical_url: https://wso2.com/api-platform/docs/ai-gateway/streaming-responses/ +md_url: https://wso2.com/api-platform/docs/ai-gateway/streaming-responses.md +tags: + - ai-gateway + - llm + - mcp + - streaming +author: WSO2 API Platform Documentation Team +last_updated: 2026-08-05 +content_type: "concept" +--- + +# Real-time AI streaming + +The AI Gateway forwards a streamed response to the client chunk by chunk, as each chunk arrives from the upstream service. The gateway doesn't hold the response until the upstream finishes generating it, so the first token reaches your application at about the same time it leaves the provider. Chat interfaces and agent loops keep their token-by-token behavior when they run through the gateway. + +Streaming applies across the gateway's artifact types: + +- **LLM providers** — a request sent straight to a provider endpoint, such as `/openai/latest/chat/completions`, streams when the upstream streams. +- **LLM proxies** — a proxy inherits the streaming behavior of the provider it consumes. +- **MCP proxies** — request bodies stream, and responses are handled differently. See [MCP proxies](#mcp-proxies). + +This page is for AI developers building on the gateway, and for platform administrators deciding which policies to attach. + +## Stream a response + +Response streaming needs no configuration on the `LlmProvider` or `LlmProxy`. The gateway streams a response whenever the upstream service streams it, so you ask for a stream the same way you would when calling the provider directly: set `"stream": true` in the request body. On an `McpProxy`, request bodies stream, but response bodies stay buffered. See [MCP proxies](#mcp-proxies). + +The following example calls an LLM proxy deployed at `/assistant`: + +```bash +curl -N -X POST "https://localhost:8443/assistant/chat/completions" \ + -H "Content-Type: application/json" \ + -d '{ + "model": "gpt-4o-mini", + "stream": true, + "messages": [ + { + "role": "user", + "content": "Write a haiku about API gateways." + } + ] + }' -k +``` + +The same request works against the provider endpoint directly. Replace `/assistant` with the provider context, such as `/openai/latest`. + +The `-N` flag turns off curl's own output buffering, so you see the Server-Sent Events (SSE) as they arrive rather than all at once at the end. + +## How policies behave on a streamed response + +Whether a response streams depends on the policies attached to the route. A policy that reads the response body either supports chunk-by-chunk processing or requires the complete body. + +**Every response-body policy on the chain must support streaming.** The gateway evaluates this per route, and it's all or nothing: + +- If every response-body policy supports streaming, the gateway streams the response to the client. +- If one policy requires the complete body, the gateway buffers the entire response, runs the chain, and then sends the response in one piece. The result is still correct, but the client waits for the last token before it sees the first. + +The chain spans both levels. For a request through an LLM proxy, it covers the organization-wide policies the platform administrator attached to the `LlmProvider` and the per-application policies the developer attached to the `LlmProxy`. A buffered-only policy at either level buffers the response. + +Policies that don't read the response body — authentication, request-side rate limiting, header policies, prompt management — never affect streaming. + +### Gating policies + +A streaming-capable policy can still hold bytes back when it has to. A guardrail that enforces a minimum, such as a minimum sentence count, can't rule on content it hasn't seen. Such a policy accumulates chunks silently until it has enough content to decide, releases what it has accumulated, and then processes each later chunk as it arrives. The client sees a pause at the start of the response rather than a wait for the whole response. + +### MCP proxies + +Response bodies on MCP proxies stay buffered, even when the MCP server replies with a streamed body. The gateway runs the response chain against the complete body and then sends it. Request bodies on MCP proxies stream under the same rules as any other route. + +## Analytics on a streamed response + +Analytics doesn't cost you the streaming behavior. The client receives every chunk at the time it arrives, and the gateway emits one analytics event for the request once the stream closes. The gateway emits that event for every stream it closes, including a stream that carries no `usage` block. In that case, the event records the request without token counts. See [Token usage on a streamed response](#token-usage-on-a-streamed-response). + +## Token usage on a streamed response + +Token counts drive analytics, cost tracking, and token-based rate limiting on LLM traffic. On a streamed response, the gateway reads them from the `usage` block that the provider sends in the stream, which arrives in the final events rather than in every chunk. + +Providers differ in when they send that block: + +- **OpenAI-compatible providers** omit `usage` unless the client asks for it. Add `stream_options` to the request: + + ```json + { + "model": "gpt-4o-mini", + "stream": true, + "stream_options": { "include_usage": true }, + "messages": [{ "role": "user", "content": "Write a haiku about API gateways." }] + } + ``` + +- **Anthropic** reports token counts in its `message_start` and `message_delta` events, so no extra request field is needed. + +If a streamed response carries no `usage` block, the gateway has no token counts to record for that request. The analytics event still reports the request, with its token metrics empty, and cost calculation and token-based rate limiting have nothing to work with. Set `stream_options` on OpenAI-compatible requests whenever you rely on any of those, including when the budget controls on the `LlmProvider` use token-based rate limiting. + +## Related documentation + +- [LLM Proxy Quick Start Guide](llm-proxy/quick-start-guide.md) — deploy a provider and a proxy, then send your first request +- [MCP Proxy Quick Start Guide](mcp-proxy/quick-start-guide.md) — deploy an MCP proxy +- [Sentence Count Guardrail](llm-proxy/guardrails/sentence-count.md) — a guardrail that gates a stream until it can evaluate the content diff --git a/en/docs/api-gateway/1.2.0/about-this-release.md b/en/docs/api-gateway/1.2.0/about-this-release.md new file mode 100644 index 000000000..9852e2fd9 --- /dev/null +++ b/en/docs/api-gateway/1.2.0/about-this-release.md @@ -0,0 +1,119 @@ +# API Gateway Changelog + +**Release date:** 2026-07-07 +**Previous version:** 1.1.0 (2026-04-30) + +### New feature additions + +- **Traffic logging support:** Added traffic logging capabilities for gateway request and response flows. +- **Timeout functionality support:** Added timeout configuration support for gateway traffic handling. +- **MSSQL support for gateway:** Added Microsoft SQL Server support for gateway persistence. +- **Multiple virtual host support:** The `endpoints` array replaces the single `vhost` field, allowing one gateway to expose multiple virtual hosts simultaneously ([#2128](https://github.com/wso2/api-platform/issues/2128)). +- **`upstreamDefinitions` URL improvements:** Upstream definition URLs support query strings and path fragments correctly ([#2068](https://github.com/wso2/api-platform/issues/2068)). +- **Kubernetes Gateway API conformance support:** Added Kubernetes Gateway API conformance support. +- **Multi-provider routing:** Added routing support across multiple providers. + +#### New policies + +- **[Opaque token authentication](https://wso2.com/api-platform/policy-hub/policies/opaque-token-auth)** +- **[Backend JWT](https://wso2.com/api-platform/policy-hub/policies/backend-jwt)** +- **[AWS authentication](https://wso2.com/api-platform/policy-hub/policies/aws-authentication):** +- **[MCP ratelimit](https://wso2.com/api-platform/policy-hub/policies/mcp-ratelimit)** +- **[Granite Guardian Prompt Injection](https://wso2.com/api-platform/policy-hub/policies/granite-guardian-prompt-injection)** +- **[OpenAI to Anthropic Transformer](https://wso2.com/api-platform/policy-hub/policies/openai-to-anthropic-transformer)** +- **[OpenAI to Azure OpenAI Transformer](https://wso2.com/api-platform/policy-hub/policies/openai-to-azure-openai-transformer)** +- **[OpenAI to Bedrock Transformer](https://wso2.com/api-platform/policy-hub/policies/openai-to-bedrock-transformer)** +- **[OpenAI to Gemini Transformer](https://wso2.com/api-platform/policy-hub/policies/openai-to-gemini-transformer)** +- **[OpenAI to Mistral Transformer](https://wso2.com/api-platform/policy-hub/policies/openai-to-mistral-transformer)** + +See [Policy Hub](https://wso2.com/api-platform/policy-hub) for policy references. + +### Added + +- `feat(llm)`: Multi-provider routing for LLM proxies ([9288a1e20](https://github.com/wso2/api-platform/commit/9288a1e20)). +- `feat(gateway)`: Multi-provider model round-robin routing ([2878f7b90](https://github.com/wso2/api-platform/commit/2878f7b90)). +- Added AWS Bedrock multi-provider support ([6da796b0e](https://github.com/wso2/api-platform/commit/6da796b0e)). +- Added opt-in `pprof` endpoints on admin servers ([da3d42c0b](https://github.com/wso2/api-platform/commit/da3d42c0b)). +- Added global traffic logging support and field-exclusion controls ([d484dd7bb](https://github.com/wso2/api-platform/commit/d484dd7bb), [bd2f451f7](https://github.com/wso2/api-platform/commit/bd2f451f7), [58eb41727](https://github.com/wso2/api-platform/commit/58eb41727)). +- Added support for request/response body and header capture options in traffic logging ([8a3b33f4f](https://github.com/wso2/api-platform/commit/8a3b33f4f)). +- Added policy-engine context snapshots for downstream path/method and upstream status ([61a68890e](https://github.com/wso2/api-platform/commit/61a68890e)). +- Added policy metadata support for analytics/traffic logging ([6ad01d3b3](https://github.com/wso2/api-platform/commit/6ad01d3b3)). +- Added SQL Server support and related schema/distribution updates ([505abca5f](https://github.com/wso2/api-platform/commit/505abca5f), [4707e2b38](https://github.com/wso2/api-platform/commit/4707e2b38), [9eff6defd](https://github.com/wso2/api-platform/commit/9eff6defd), [c70f3ff1c](https://github.com/wso2/api-platform/commit/c70f3ff1c)). +- Added gateway health check implementation ([4fea1ce05](https://github.com/wso2/api-platform/commit/4fea1ce05)). +- Added basic auth protection for admin service (health endpoint excluded) ([ac2129858](https://github.com/wso2/api-platform/commit/ac2129858)). +- Added role-based admin validation and enforced role-claims when IDP is enabled ([d53b8844d](https://github.com/wso2/api-platform/commit/d53b8844d), [bf8342c62](https://github.com/wso2/api-platform/commit/bf8342c62)). +- Added support for multiple `-config` files in both controller and policy-engine ([c6b343a87](https://github.com/wso2/api-platform/commit/c6b343a87), [073cb77bf](https://github.com/wso2/api-platform/commit/073cb77bf)). +- Added `set-headers` append mode support (policy update from [#2103](https://github.com/wso2/api-platform/issues/2103)). +- Added HTTP connection manager (downstream) timeout config support in runtime config model. + +### Changed + +- Refactored policy definition sourcing to use gateway-builder output ([0f8b59aeb](https://github.com/wso2/api-platform/commit/0f8b59aeb)). +- Updated routing model: moved header-based routing and redirect logic out of API YAML into dedicated policies ([22198fd11](https://github.com/wso2/api-platform/commit/22198fd11), [eec5e2fa7](https://github.com/wso2/api-platform/commit/eec5e2fa7), [504d33bdf](https://github.com/wso2/api-platform/commit/504d33bdf), [d0f84a780](https://github.com/wso2/api-platform/commit/d0f84a780)). +- Updated operation routing model to use a new `match` object for path match type and header-based matching (from [#2103](https://github.com/wso2/api-platform/issues/2103)). +- Enabled path normalization by default ([c7979cf16](https://github.com/wso2/api-platform/commit/c7979cf16)). +- Updated policy bundles/versions, including API key and auth policy lines ([f67181bb5](https://github.com/wso2/api-platform/commit/f67181bb5), [ff1965e9b](https://github.com/wso2/api-platform/commit/ff1965e9b), [cb788c033](https://github.com/wso2/api-platform/commit/cb788c033), [855da688b](https://github.com/wso2/api-platform/commit/855da688b), [d573b07a8](https://github.com/wso2/api-platform/commit/d573b07a8), [c7553aa1a](https://github.com/wso2/api-platform/commit/c7553aa1a)). +- Refined config interpolation and overrides, including env/file interpolation and allowlist behavior ([bff0bc3ee](https://github.com/wso2/api-platform/commit/bff0bc3ee), [47e74040d](https://github.com/wso2/api-platform/commit/47e74040d), [eca558827](https://github.com/wso2/api-platform/commit/eca558827), [4a9a263a7](https://github.com/wso2/api-platform/commit/4a9a263a7)). +- Updated runtime/build baselines (Go toolchain and Envoy updates) ([f4ff5034c](https://github.com/wso2/api-platform/commit/f4ff5034c), [a38b95f5f](https://github.com/wso2/api-platform/commit/a38b95f5f), [be297cf32](https://github.com/wso2/api-platform/commit/be297cf32)). +- Included distribution docs and packaging updates for release artifacts ([41f89cde7](https://github.com/wso2/api-platform/commit/41f89cde7), [c70f3ff1c](https://github.com/wso2/api-platform/commit/c70f3ff1c)). +- Bumped gateway controller REST API base paths to `v1` and aligned artifact API versioning to `gateway.api-platform.wso2.com/v1`. +- Added `data_version` handling for gateway-controller artifacts to decouple stored data shape from wire version ([290606d8a](https://github.com/wso2/api-platform/commit/290606d8a), [70a86c6e3](https://github.com/wso2/api-platform/commit/70a86c6e3)). +- Changed custom policy `managedBy` default/normalized value from `customer` to `organization`. +- Updated analytics/collector config model: renamed gRPC collector section and split payload controls for request/response bodies and headers. +- Moved toward config-driven env/file interpolation for runtime config management via `config.toml` templates, plus [bff0bc3ee](https://github.com/wso2/api-platform/commit/bff0bc3ee) and [47e74040d](https://github.com/wso2/api-platform/commit/47e74040d). + +### Config Changes + +- Added new `traffic_logging` config block: + +```toml +[traffic_logging] +enabled = false +masked_headers = ["authorization", "x-api-key", "x-jwt-assertion"] +max_payload_size = 0 +request_headers = false +request_body = false +response_headers = false +response_body = false + +[traffic_logging.properties] +``` + +- Collector and analytics config changes: + - Renamed `[analytics.gprc_event_server]` to `[collector.server]`. + - Removed `[analytics.gprc_event_server].server_port`. + - Replaced analytics payload toggles with collector-level request/response controls: + +```toml +[collector] +request_body = false +response_body = false +request_headers = false +response_headers = false +ignored_path_prefixes = [] +``` + +- Added downstream HTTP listener timeout block: + +```toml +[router.http_listener.timeouts] +request_timeout = "0s" +request_headers_timeout = "0s" +stream_idle_timeout = "5m" +idle_timeout = "1h" +``` + +- Configuration source model update: direct `APIP_*` runtime overrides are removed in favor of `{{ env }}` / `{{ file }}` interpolation in `config.toml`, with required env vars supplied via env files. + +### Fixed + +- Fixed stale extracted policy artifacts in gateway runtime ([6b07c59c4](https://github.com/wso2/api-platform/commit/6b07c59c4)). +- Fixed controller behavior for overlapping/same-name LLM policy matches ([746db0ed9](https://github.com/wso2/api-platform/commit/746db0ed9), [14451b47e](https://github.com/wso2/api-platform/commit/14451b47e)). +- Fixed dynamic endpoint/base path handling in gateway and controller ([7cff05d2f](https://github.com/wso2/api-platform/commit/7cff05d2f), [30a24d2b7](https://github.com/wso2/api-platform/commit/30a24d2b7), [ad921c208](https://github.com/wso2/api-platform/commit/ad921c208)). +- Fixed `upstreamDefinitions` URL validation and error clarity (query/fragment rejection) ([227e19bc5](https://github.com/wso2/api-platform/commit/227e19bc5), [d5bc0b535](https://github.com/wso2/api-platform/commit/d5bc0b535), [d267f3346](https://github.com/wso2/api-platform/commit/d267f3346), [a43762260](https://github.com/wso2/api-platform/commit/a43762260)). +- Fixed xDS snapshot update race with synchronization improvements ([62b7f22a8](https://github.com/wso2/api-platform/commit/62b7f22a8), [b87094a43](https://github.com/wso2/api-platform/commit/b87094a43)). +- Fixed graceful runtime shutdown by draining Router ([271cc4c2b](https://github.com/wso2/api-platform/commit/271cc4c2b)). +- Fixed duplicate analytics event emission in LLM proxy flows and hardened loopback suppression ([995a97edc](https://github.com/wso2/api-platform/commit/995a97edc), [07c5fe644](https://github.com/wso2/api-platform/commit/07c5fe644), [9b4f54d23](https://github.com/wso2/api-platform/commit/9b4f54d23), [447d5a7bb](https://github.com/wso2/api-platform/commit/447d5a7bb)). +- Fixed policy-engine update behavior to retain unchanged chains and avoid false route removals ([edb2e453d](https://github.com/wso2/api-platform/commit/edb2e453d), [066695c7a](https://github.com/wso2/api-platform/commit/066695c7a)). +- Fixed LLM provider validation/sync edge cases, including duplicate-named API-level policy preservation and invalid config rejection ([6ce9cbde3](https://github.com/wso2/api-platform/commit/6ce9cbde3), [2843f9eb5](https://github.com/wso2/api-platform/commit/2843f9eb5)). +- Fixed translator/runtime handling for timeout mapping and upstream selection continuity ([34a82c1bc](https://github.com/wso2/api-platform/commit/34a82c1bc), [6e79649c7](https://github.com/wso2/api-platform/commit/6e79649c7), [0a98f265b](https://github.com/wso2/api-platform/commit/0a98f265b)). diff --git a/en/docs/api-gateway/1.2.0/analytics/analytics-header-filter.md b/en/docs/api-gateway/1.2.0/analytics/analytics-header-filter.md new file mode 100644 index 000000000..2cb2f9057 --- /dev/null +++ b/en/docs/api-gateway/1.2.0/analytics/analytics-header-filter.md @@ -0,0 +1,127 @@ +--- +title: "Analytics Header Filter Policy" +description: "Control which request and response headers are captured in analytics using allow or deny mode in API Platform Gateway." +canonical_url: https://wso2.com/api-platform/docs/api-gateway/analytics/analytics-header-filter/ +md_url: https://wso2.com/api-platform/docs/api-gateway/analytics/analytics-header-filter.md +tags: + - api-gateway + - analytics + - observability +author: WSO2 API Platform Documentation Team +last_updated: 2026-06-17 +content_type: "reference" +--- + +# Analytics Header Filter + +## Overview + +The Analytics Header Filter policy allows you to control which request and response headers are included in analytics data using allow or deny modes. This policy is intended to prevent sensitive, noisy, or irrelevant headers from being sent to analytics backends while preserving the rest of the request and response context. + +The policy is only effective when analytics is enabled at the system level and must be explicitly added to the API’s policy chain. + +**Operation modes:** +- **"allow"**: Only the specified headers will be included in analytics (whitelist mode) +- **"deny"**: All headers except the specified ones will be included in analytics (blacklist mode) + +Request and response headers can have different operation modes, allowing for flexible filtering strategies. + + +## Features + +* Filters request and response headers from analytics data collection using allow or deny modes +* Case-insensitive header matching +* Supports independent configuration with flexible filtering strategies with whitelist (allow) and blacklist (deny) modes +* Operates transparently without affecting request or response processing +* Helps protect sensitive information from being exposed in analytics systems + + +## Configuration + +### Parameters + +| Parameter | Type | Required | Default | Description | +| ------------------------- | ------ | -------- | ------- | ---------------------------------------------------------------------------------------------------------- | +| `requestHeadersToFilter` | object | No | - | Configuration for filtering request headers. Contains `operation` and `headers` properties. | +| `responseHeadersToFilter` | object | No | - | Configuration for filtering response headers. Contains `operation` and `headers` properties. | + +### Parameter Structure + +Each filter parameter (`requestHeadersToFilter` and `responseHeadersToFilter`) is an object with the following properties: + +| Property | Type | Required | Description | +| ----------- | ------ | -------- | ---------------------------------------------------------------------------------------------------------- | +| `operation` | string | Yes | Operation mode: `"allow"` (whitelist) or `"deny"` (blacklist). Header names are matched case-insensitively. | +| `headers` | array | Yes | List of header names to filter. Behavior depends on the operation mode. Each header name must be 1-256 characters. | + +> **Note**: This policy only affects analytics data collection. It does not remove or modify headers sent to upstream services or returned to clients. + + +## System Requirements + +* Analytics must be enabled globally via `config.yaml` (`analytics.enabled: true`) +* The policy must be explicitly applied to the API policy chain +* If analytics is disabled at the system level, this policy has no effect + + +## API Definition Example + +The following example demonstrates how to apply the Analytics Header Filter policy to a REST API: + +```bash +curl -X POST http://localhost:9090/api/management/v0.9/rest-apis \ + -H "Content-Type: application/yaml" \ + --data-binary @- <<'EOF' +apiVersion: gateway.api-platform.wso2.com/v1alpha1 +kind: RestApi +metadata: + name: mock-api-v1.0 +spec: + displayName: Mock-API + version: v1.0 + context: /mockapi/$version + upstream: + main: + url: https://api-platform162t63.example.io/api/v1 + policies: + - name: analytics-header-filter + version: v1 + params: + requestHeadersToFilter: + operation: deny + headers: + - "x-wso2-api-name" + - "Authorization" + responseHeadersToFilter: + operation: deny + headers: + - "content-type" + operations: + - method: GET + path: /comments + - method: GET + path: /features +EOF +``` + +## Use Cases + +- **Sensitive Data Protection**: Prevent authentication tokens, internal identifiers, or security-related headers from being sent to analytics systems. + +- **Noise Reduction**: Exclude verbose or low-value headers to improve the clarity and usefulness of analytics data. + +- **Compliance and Governance**: Support compliance requirements by ensuring certain headers are never exported outside the platform. + +- **Cost and Storage Optimization**: Reduce analytics payload size by removing unnecessary headers from published events. + + +## Notes + +* Header name matching is case-insensitive. +* The `operation` field is required and must be either `"allow"` or `"deny"`. +* The `headers` array is required but can be empty. When the array is empty, all original headers are included(if allowed explicitly) in analytics for both `"allow"` and `"deny"` modes (safe fallback behavior). +* Request and response headers can use different operation modes independently. +* This policy does not block requests or responses. +* Filtering applies only to analytics collection, not to runtime request handling. +* The policy must be applied per API and does not operate implicitly. + diff --git a/en/docs/api-gateway/1.2.0/analytics/moesif-analytics.md b/en/docs/api-gateway/1.2.0/analytics/moesif-analytics.md new file mode 100644 index 000000000..5d2361fd0 --- /dev/null +++ b/en/docs/api-gateway/1.2.0/analytics/moesif-analytics.md @@ -0,0 +1,174 @@ +--- +title: "Configure Moesif Analytics" +description: "Configure the Moesif in API Platform Gateway to capture and publish API request and response data." +canonical_url: https://wso2.com/api-platform/docs/api-gateway/analytics/moesif-analytics/ +md_url: https://wso2.com/api-platform/docs/api-gateway/analytics/moesif-analytics.md +tags: + - api-gateway + - analytics + - moesif + - observability +author: WSO2 API Platform Documentation Team +last_updated: 2026-06-17 +content_type: "how-to" +--- + +# Analytics + +## Overview + +The Analytics feature enables the API Platform to capture, process, and publish API request and response data for observability and business insights. Analytics data is collected asynchronously from the gateway without impacting request latency and is published to an external analytics platform for further analysis and visualization. + +This capability allows platform administrators and business stakeholders to gain visibility into API usage patterns, traffic behavior, latency characteristics, and consumer activity across the platform. + + +## Features + +* Asynchronous collection of API request and response data +* Policy-enriched analytics metadata capture +* Zero impact on request/response latency +* Batched and configurable publishing to external analytics platforms +* Horizontally scalable analytics processing pipeline +* Pluggable publisher model (supports multiple analytics backends) + + +## Prerequisites + +- Active Moesif Account and an Application ID. + For obtaining the Application ID, follow these steps: + - Step 1: Sign up in [Moesif](https://www.moesif.com/) + - Sept 2: Follow the onboarding wizard. + - Sept 3: During the sign up process, you will receive a Collector Application ID for your configured application. Copy this value and keep it saved. + +!!! note + For more detailed instructions and advanced configuration options, refer to the [official Moesif Documentation](https://www.moesif.com/docs). + + +## Configuration + +Analytics is configured entirely through the gateway `config.toml` file and is enabled at a system level. + +!!! note + Analytics is a **consumer** of a shared data-capture pipeline called the **collector**. The + collector has no `enabled` flag of its own — it activates automatically whenever `analytics.enabled` + (or `traffic_logging.enabled`) is `true`. See [Traffic Logging](../observability/traffic-logging.md) + for the other consumer of this same pipeline, which writes a stdout JSON line per request instead + of publishing to an external SaaS. + +### System Parameters (`config.toml`) + +#### Analytics + +| Parameter | Type | Required | Default | Description | +| --------- | ------- | -------- |-------- | -------------------------------------- | +| `enabled` | boolean | Yes | false | Enables or disables analytics globally, and activates the collector. | +| `enabled_publishers` | array of strings | No | `["moesif"]` | Publisher names to activate. Currently only `moesif` is supported. | + +#### Publishers + +Each entry in `enabled_publishers` must have a matching configuration table under +`[analytics.publishers.]`. + +##### Moesif (`[analytics.publishers.moesif]`) + +| Parameter | Type | Required | Description | +| ---------------------- | ------- | -------- | ----------------------------------------- | +| `application_id` | string | Yes | Moesif application identifier | +| `moesif_base_url` | string | No | Moesif ingestion endpoint (default `https://api.moesif.net`) | +| `publish_interval` | int | Yes | Interval (seconds) between publish cycles | +| `event_queue_size` | int | Yes | Maximum events held in memory | +| `batch_size` | int | Yes | Maximum events per batch | +| `timer_wakeup_seconds` | int | Yes | Publisher timer resolution | + +#### Data capture (`[collector]`) + +Analytics captures request/response headers and bodies through the shared `[collector]` section (also +used by Traffic Logging). Capture is off by default — enable only what Moesif needs: + +| Parameter | Type | Default | Description | +|---|---|---|---| +| `request_body` | boolean | `false` | Capture the full request body and attach it to published events. | +| `response_body` | boolean | `false` | Capture the full response body and attach it to published events. | +| `request_headers` | boolean | `false` | Capture all request headers and attach them to published events. | +| `response_headers` | boolean | `false` | Capture all response headers and attach them to published events. | +| `ignore_path_prefixes` | array of strings | `[]` | Path prefixes (e.g. `/health`, `/metrics`) for which no analytics event is produced at all. | + +!!! note + Masking/redaction is a per-consumer, presentation-time concern, not a collector guarantee — the + collector hands every enabled consumer the same raw captured data. Moesif always receives unmasked + headers, regardless of any `masked_headers` configuration used by Traffic Logging. + +#### ALS transport (`[collector.server]`) + +This section configures both the Envoy access log streaming settings and the ALS (Access Log Service) +server that receives those logs. It is shared with Traffic Logging — the controller reads it to +configure Envoy's gRPC access-log sink, and the policy-engine reads it to configure the receiving ALS +server. + +| Parameter | Type | Required | Default | Description | +| ----------------------- | -------- | -------- |---- | -------------------------------- | +| `mode` | string | No | `"uds"` | Transport mode: `"uds"` (Unix domain socket) or `"tcp"`. | +| `buffer_flush_interval` | duration | No | `1000000000`| Maximum time Envoy waits(in nanoseconds) before flushing buffered access log entries.| +| `buffer_size_bytes` | int | No | `16384` | Maximum size of the in-memory buffer used to batch access log entries before sending them to ALS server. | +| `grpc_request_timeout` | duration | No | `20000000000` | Timeout duration Envoy waits(in nanoseconds) for a response from the ALS server before considering the log delivery attempt failed. | +| `shutdown_timeout` | duration | No | `"600s"` | Maximum time allowed for the ALS server to gracefully shut down while completing in-flight log processing. | +| `als_plain_text` | boolean | No | `true` | Use plaintext gRPC | +| `public_key_path` | string | No | - | Path to the public key used for securing ALS communication when transport-level encryption or authentication is enabled. | +| `private_key_path` | string | No | - | Path to the private key used for securing ALS communication when transport-level encryption or authentication is enabled. | +| `max_message_size` | int | No | `1000000000` |Maximum size of a single gRPC message that the ALS server is allowed to receive from Envoy. | +| `max_header_limit` | int | No | `8192` | Maximum allowed size of request or response headers processed by the ALS server | + +!!! note + The ALS gRPC port is fixed at `18090` and is not configurable — this guarantees the controller and + policy-engine sides can never disagree on it. The hostname for the ALS connection is automatically + derived from the policy-engine configuration. The internal log name identifier is set to + `"envoy_access_log"` and is not configurable. + +#### Deprecated keys + +The following `[analytics]` keys still work, but are deprecated in favor of the shared `[collector]` / +`[collector.server]` sections above. They are migrated automatically at load time with a warning, so +existing configs keep working unchanged: + +| Deprecated key | Migrated onto | Notes | +|---|---|---| +| `analytics.allow_payloads` | `collector.request_body` + `collector.response_body` | Directional flags (below) win over `allow_payloads`. | +| `analytics.send_request_body` / `send_response_body` | `collector.request_body` / `response_body` | — | +| `analytics.grpc_event_server.*` | `collector.server.*` | Whole-section migration, applied only while `[collector.server]` is unset and `analytics.enabled = true`. | +| `analytics.access_logs_service.*` | `collector.server.*` | Same migration as above; `server_port` is dropped since the ALS port is now a fixed constant. | + + +## Configuration Examples + +#### Integrate Moesif Publisher + +```toml +[collector] +request_headers = true +response_headers = true + +[collector.server] +mode = "uds" + +[analytics] +enabled = true +enabled_publishers = ["moesif"] + +[analytics.publishers.moesif] +application_id = "" +moesif_base_url = "https://api.moesif.net" +publish_interval = 5 +event_queue_size = 10000 +batch_size = 50 +timer_wakeup_seconds = 3 +``` + + +## Use Cases + +* **API Usage Visibility** – Understand how APIs are consumed across tenants and applications. +* **Operational Insights** – Observe traffic volume, response behavior, and latency trends. +* **Business Intelligence** – Support product and business decisions using API analytics data. +* **Platform Monitoring** – Gain observability into API behavior without impacting performance. + + diff --git a/en/docs/api-gateway/1.2.0/deployment/deploying-apis/bottom-up-api-deployment.md b/en/docs/api-gateway/1.2.0/deployment/deploying-apis/bottom-up-api-deployment.md new file mode 100644 index 000000000..da96dbcbf --- /dev/null +++ b/en/docs/api-gateway/1.2.0/deployment/deploying-apis/bottom-up-api-deployment.md @@ -0,0 +1,680 @@ +--- +title: "Bottom-Up API Deployment Guide" +description: "Deploy REST APIs directly to API Platform Gateway and automatically sync them to on-prem WSO2 API Manager using bottom-up deployment." +canonical_url: https://wso2.com/api-platform/docs/api-gateway/deployment/deploying-apis/bottom-up-api-deployment/ +md_url: https://wso2.com/api-platform/docs/api-gateway/deployment/deploying-apis/bottom-up-api-deployment.md +tags: + - api-gateway + - deployment + - api-management +author: WSO2 API Platform Documentation Team +last_updated: 2026-06-11 +content_type: "how-to" +--- + +# Bottom-Up API Deployment Guide + +## Overview + +When you deploy a REST API via the gateway controller's REST API endpoint, the gateway **automatically tracks it for bottom-up sync** to on-prem WSO2 APIM 4.7.x (if configured for on-prem control plane mode). + +### Understanding API Deployment Approaches + +The WSO2 API Platform gateway supports two fundamentally different deployment approaches, distinguished by the **direction of API flow**: + +#### **Top-Down Deployment (Control Plane → Gateway)** + +In top-down deployment, the **platform-API (central control plane) pushes APIs to the gateway** via WebSocket connection. + +**How it works:** +- Control plane is the source of truth +- Platform-API sends API definitions via WebSocket +- Gateway receives and applies these definitions +- Gateway pulls configuration from control plane + +#### **Bottom-Up Deployment (Gateway → On-Prem APIM)** + +Currently this support is only available for the on prem APIM control plane type. + +In bottom-up deployment, **REST APIs deployed directly to the gateway are automatically synced back to on-prem WSO2 APIM.** (If configured with on prem control plane type. Cloud control plane is not having this support at the moment.). + +**How it works:** +- Gateway is the initial deployment point +- API is deployed via `POST /rest-apis` REST endpoint +- Gateway makes API available immediately +- Gateway then syncs the API to on-prem APIM (if connected) + +**Characteristics:** +- **Data flow:** Gateway → On-Prem APIM +- **Use case:** On-prem APIM integration +- **Sync:** Automatic and tracked (pending/success/failed) +- **Status tracking:** Yes, tracked internally and observable via gateway controller logs +- **Continues working:** API remains available on gateway even if APIM is unavailable + + +#### **Key Differences** + +| Aspect | Top-Down | Bottom-Up | +|--------|----------|-----------| +| **Initiation** | Control plane (platform-API) | Gateway (REST API) | +| **Direction** | Platform-API → Gateway | Gateway → On-Prem APIM | +| **Source of Truth** | Central control plane | Local gateway | +| **APIM Integration** | Not applicable | Syncs to on-prem APIM | +| **Status Tracking** | Managed by platform-API | Tracked in gateway DB | +| **Failure Handling** | Depends on platform-API | API works, sync retries | + +--- + +**This guide focuses on Bottom-Up deployment** with automatic sync to on-prem APIM. + +## Prerequisites + +### Gateway Controller Requirements + +- Gateway controller running locally or remotely +- Management API endpoint: `http://localhost:9090/api/management/v0.9` (default) + +### Bottom-Up API Sync Requirements + +- On-prem WSO2 APIM instance +- Control plane configuration with on-prem mode enabled +- Network connectivity between gateway controller and APIM +- APIM OAuth2 credentials (client ID/secret or username/password) + +--- + +## Configuration + +### Gateway Controller Configuration + +**File:** `config.toml` + +{% raw %} +```toml +[controller.server] +gateway_id = "gateway-1" +port = 9090 + +[controller.controlplane] +# For bottom-up sync to on-prem APIM +host = "192.168.0.102:9443" +insecure_skip_verify = true + +# OAuth2 credentials (choose one option) +# Option A: Client credentials flow +apim_oauth2_client_id = "" +apim_oauth2_client_secret = "" + +# Option B: Resource owner password flow +# apim_oauth2_username = "admin" +# apim_oauth2_password = "admin" + +gateway_name = "onprem-gw" + +[controller.auth.basic] +enabled = true + +[[controller.auth.basic.users]] +username = '{{ env "APIP_GW_CONTROLLER_AUTH_BASIC_ADMIN_USERNAME" "" }}' +password = '{{ env "APIP_GW_CONTROLLER_AUTH_BASIC_ADMIN_PASSWORD_HASH" "" }}' +password_hashed = true +roles = ["admin"] +``` +{% endraw %} + +--- + +## How It Works + +### Sync Behavior + +When you deploy a REST API via the gateway controller: + +1. **API is deployed to the gateway immediately** + - Available for routing + - API keys can be created + - Requests are processed + +2. **Sync to On-Prem APIM (if configured)** + - Only happens if `controller.controlplane.host` is configured + - Only happens if gateway is connected to on-prem APIM + - Status tracked in database: `pending` → `success`/`failed` + - Automatic retries (3 attempts) on failure + +### Conditions for Sync + +All three must be true: + +```go +controlPlaneClient != nil && // APIM config exists +controlPlaneClient.IsConnected() && // WebSocket connected to APIM +controlPlaneClient.IsOnPrem() // On-prem mode detected (via .well-known endpoint) +``` + +### What Happens If Sync Fails + +- API **remains available on the gateway** +- Status marked as `failed` with error details +- Automatic retry on next API update +- Manual retry: Update the API with same definition + +--- + +## REST API Deployment + +Bottom-up APIs are REST APIs deployed via the gateway controller that are **automatically synced** to on-prem WSO2 APIM. + +### Key Features + +- All REST APIs deployed via gateway are tracked for bottom-up sync +- Automatic sync to on-prem APIM (if connected) +- No manual push required - happens in background +- Sync status tracking (pending, success, failed) +- Automatic retry logic (3 attempts on failure) +- Support for API updates and undeployment +- Continues working even if APIM is temporarily unavailable + +### Deployment Flow + +``` +┌─────────────────────────────────────────────────┐ +│ REST API Deployed via POST /rest-apis │ +└────────────────────┬────────────────────────────┘ + │ + ▼ (Origin = "gateway_api") + ┌─────────────────────┐ + │ Store in Database │ + │ CPSyncStatus: │ + │ pending │ + └─────────┬───────────┘ + │ + ┌──────────┴──────────┐ + │ │ + ▼ ▼ + ┌─────────┐ ┌───────────────┐ + │ Gateway │ │ Check Control │ + │ Ready │ │ Plane Status │ + │ (API │ │ - Connected? │ + │ Routing)│ │ - On-prem? │ + └─────────┘ └───────┬───────┘ + │ │ + │ ┌──────┴────────┐ + │ │ │ + │ ▼ ▼ + │ ┌──────────┐ ┌─────────────┐ + │ │ Sync to │ │ API works │ + │ │ APIM │ │ locally but │ + │ │ success │ │ no APIM │ + │ │ status: │ │ sync (wait) │ + │ │ success │ └─────────────┘ + │ └──────────┘ + │ + └─────────────────────────┐ + │ + ▼ + ┌────────────────┐ + │ API Available │ + │ on Gateway: │ + │ - Routing │ + │ - API Keys │ + │ - Policies │ + └────────────────┘ +``` + +### API Definition + +All REST APIs deployed via the gateway will be synced to on-prem APIM automatically. Here's a complete example: + +```yaml +apiVersion: gateway.api-platform.wso2.com/v1alpha1 +kind: RestApi +metadata: + name: PetStoreAPI +spec: + displayName: PetStore API + version: v1.0 + context: /petstore + + upstream: + main: + url: "https://petstore.example.com" + + # API-level policies apply to all operations + policies: + - name: api-key-auth + version: v1 + params: + key: X-API-Key + in: header + + - name: basic-ratelimit + version: v1 + params: + limits: + - requests: 1000 + duration: 1h + + operations: + - method: GET + path: /pet/{petId} + # Operation-level policies override API-level + policies: + - name: basic-ratelimit + version: v1 + params: + limits: + - requests: 100 + duration: 1m + + - method: POST + path: /pet + + - method: PUT + path: /pet + + - method: DELETE + path: /pet/{petId} + + - method: GET + path: /store/inventory + + - method: POST + path: /store/order + + - method: GET + path: / +``` + +**Important Notes:** + +- `metadata.name` must be unique per API +- `context` becomes the URL path prefix on the gateway +- `upstream.main.url` is required (backend service) +- `version` is tracked separately (can have multiple versions of same API) +- Policies are optional but recommended for security + +### Deployment Steps + +**Step 1: Create API YAML** + +Save the API definition as `petstore-api.yaml`: + +```yaml +apiVersion: gateway.api-platform.wso2.com/v1alpha1 +kind: RestApi +metadata: + name: PetStoreAPI +spec: + displayName: PetStore API + version: v1.0 + context: /petstore + upstream: + main: + url: https://petstore.example.com + policies: + - name: api-key-auth + version: v1 + params: + key: X-API-Key + in: header + operations: + - method: GET + path: /pet/{petId} + - method: POST + path: /pet + - method: PUT + path: /pet + - method: DELETE + path: /pet/{petId} + - method: GET + path: /store/inventory + - method: POST + path: /store/order + - method: GET + path: / +``` + +**Step 2: Deploy to Gateway** + +!!! note + The examples below use a `BASE64_CREDENTIALS` environment variable for the Basic auth header. Set it from the admin credentials `scripts/setup.sh` provisioned — the username defaults to `admin`; use the password it printed: + ```bash + export ADMIN_USERNAME=admin + export ADMIN_PASSWORD='' + export BASE64_CREDENTIALS=$(echo -n "$ADMIN_USERNAME:$ADMIN_PASSWORD" | base64) + ``` + +```bash +curl -X POST http://localhost:9090/api/management/v0.9/rest-apis \ + -H "Content-Type: application/yaml" \ + -H "Authorization: Basic ${BASE64_CREDENTIALS}" \ + --data-binary @petstore-api.yaml +``` + +**Response:** +```json +{ + "uuid": "api-uuid-12345", + "displayName": "PetStore API", + "version": "v1.0", + "context": "/petstore", + "origin": "gateway_api", + "desiredState": "deployed", + "createdAt": "2026-04-26T10:30:00Z" +} +``` + +!!! note + Check the gateway controller logs to confirm control plane sync status. Look for `"Bottom-up sync: starting"` and `"Bottom-up sync: API synced successfully"` entries. If sync fails, the log will show the error detail and the gateway will retry automatically. + +**Step 3: Test API on Gateway** + +```bash +# Get API key +curl -X POST http://localhost:9090/api/management/v0.9/rest-apis/PetStoreAPI/api-keys \ + -H "Content-Type: application/json" \ + -H "Authorization: Basic ${BASE64_CREDENTIALS}" \ + -d '{"name": "test-key"}' + +# Invoke API +curl http://localhost:8080/petstore/pet/123 \ + -H "X-API-Key: " +``` + +**Step 4: Verify in On-Prem APIM (Optional)** + +Once sync completes, API is available in APIM: + +```bash +# Login to APIM and check +curl -X GET https://192.168.0.102:9443/api/am/publisher/v4/apis \ + -H "Authorization: Bearer " | jq '.list[] | select(.name=="PetStore API")' +``` + +--- + +## API Updates + +### Automatic Re-Sync on Update + +When you update an API, it's automatically re-synced to on-prem APIM (if connected). + +**Example: Add Rate Limiting Policy** + +Update your API definition to add a rate limit policy and save as `petstore-api-updated.yaml`: + +```yaml +apiVersion: gateway.api-platform.wso2.com/v1alpha1 +kind: RestApi +metadata: + name: PetStoreAPI +spec: + displayName: PetStore API + version: v1.0 + context: /petstore + upstream: + main: + url: https://petstore.example.com + policies: + - name: basic-ratelimit + version: v1 + params: + limits: + - requests: 1000 + duration: 1h + - name: api-key-auth + version: v1 + params: + key: X-API-Key + in: header + operations: + - method: GET + path: /pet/{petId} +``` + +**Send Update Request:** + +```bash +curl -X PUT http://localhost:9090/api/management/v0.9/rest-apis/PetStoreAPI \ + -H "Content-Type: application/yaml" \ + -H "Authorization: Basic ${BASE64_CREDENTIALS}" \ + --data-binary @petstore-api-updated.yaml +``` + +**What Happens:** + +1. API updated on gateway immediately +2. Sync status internally reset to `pending` +3. Automatically re-synced to on-prem APIM +4. Both gateway and APIM have the updated version + +**Check Updated Status:** + +Monitor gateway controller logs for the sync result: + +```bash +# Watch for sync outcome after update +./gateway-controller 2>&1 | grep "Bottom-up sync" +``` + +--- + +## API Undeploy + +### Undeploy from Gateway and APIM + +To undeploy an API, send a DELETE request with the API name: + +```bash +curl -X DELETE http://localhost:9090/api/management/v0.9/rest-apis/PetStoreAPI \ + -H "Authorization: Basic ${BASE64_CREDENTIALS}" +``` + +**Response:** +```json +{ + "status": "success", + "message": "RestAPI deleted successfully", + "id": "PetStoreAPI" +} +``` + +--- + +## API Management Operations + +### Create API Key + +```bash +curl -X POST http://localhost:9090/api/management/v0.9/rest-apis/PetStoreAPI/api-keys \ + -H "Content-Type: application/json" \ + -H "Authorization: Basic ${BASE64_CREDENTIALS}" \ + -d '{ + "name": "my-api-key" + }' +``` + +**Response:** +```json +{ + "uuid": "key-uuid-12345", + "name": "my-api-key", + "key": "apip_39ec8f6b69ef0edaf090e11be4dbf6c379176c53cd9a86131ee3c1fc4df0a8ds", + "createdAt": "2026-04-26T10:30:00Z" +} +``` + +### Invoke Protected API + +```bash +curl http://localhost:8080/petstore/pet/1 \ + -H "X-API-Key: apip_39ec8f6b69ef0edaf090e11be4dbf6c379176c53cd9a86131ee3c1fc4df0a8ds" +``` + +### List API Keys + +```bash +curl -X GET http://localhost:9090/api/management/v0.9/rest-apis/PetStoreAPI/api-keys \ + -H "Authorization: Basic ${BASE64_CREDENTIALS}" +``` + +### Revoke API Key + +```bash +curl -X DELETE http://localhost:9090/api/management/v0.9/rest-apis/PetStoreAPI/api-keys/key-uuid-12345 \ + -H "Authorization: Basic ${BASE64_CREDENTIALS}" +``` + +--- + +## Sync Status Tracking + +### Understand Sync States + +The gateway tracks the APIM sync state internally in its database. You can monitor it via the gateway controller logs. + +| Status | Meaning | Log signal | +|--------|---------|------------| +| `pending` | Waiting to sync to APIM | `"Bottom-up sync: starting"` | +| `success` | Successfully synced to APIM | `"Bottom-up sync: API synced successfully"` | +| `failed` | Sync failed after 3 retries | `"Bottom-up sync: all retries exhausted"` | + + +### Retry Failed Sync + +If sync fails, the gateway automatically retries up to 3 times per sync cycle. Sync is re-triggered on the next control plane reconnection, or immediately when you update the API. + +To manually trigger a retry, update the API with the same definition: + +```bash +curl -X PUT http://localhost:9090/api/management/v0.9/rest-apis/PetStoreAPI \ + -H "Content-Type: application/yaml" \ + -H "Authorization: Basic ${BASE64_CREDENTIALS}" \ + --data-binary @petstore-api.yaml +``` + +--- + +## Troubleshooting + +### Issue: API Not Syncing to APIM + +**Symptom:** Gateway logs show `"Bottom-up sync: attempt failed"` or `"Bottom-up sync: all retries exhausted"` + +**Possible Causes & Solutions:** + +#### 1. Control Plane Not Configured + +```bash +# Check if host is set +echo $APIP_GW_CONTROLLER_CONTROLPLANE_HOST +# or check config.toml [controller.controlplane] section +``` + +**Fix:** Set the on-prem APIM host. These variables are read by the `env` interpolation tokens in `config.toml` (there is no `APIP_GW_` prefix override — see [Configuration and Environment Interpolation](../../setup/configuration.md)); the shipped `config.toml` already carries tokens for these keys: +```bash +export APIP_GW_CONTROLLER_CONTROLPLANE_HOST=192.168.0.102:9443 +export APIP_GW_CONTROLLER_CONTROLPLANE_APIM_OAUTH2_CLIENT_ID=... +export APIP_GW_CONTROLLER_CONTROLPLANE_APIM_OAUTH2_CLIENT_SECRET=... +``` + +#### 2. APIM Not Reachable + +**Check connectivity:** + +```bash +# Test well-known endpoint (returns gateway path for on-prem mode) +curl -k https://192.168.0.102:9443/internal/gateway/.well-known + +# Expected response: +# {"gatewayPath": "/internal/data/v1"} +``` + +**Fix:** +- Verify network connectivity to APIM host +- Check firewall rules +- Verify HTTPS/TLS settings (`insecureSkipVerify` may be needed) + +#### 3. Invalid OAuth2 Credentials + +**Check APIM logs** for authentication errors + +**Fix:** +- Verify `apim_oauth2_client_id` and `apim_oauth2_client_secret` are correct +- Ensure OAuth2 credentials have proper scopes: `apim:api_import_export apim:api_view` +- Try alternative auth method (username/password instead of client credentials) + +#### 4. Missing Upstream URL + +**Check API definition:** + +```bash +curl -X GET http://localhost:9090/api/management/v0.9/rest-apis/PetStoreAPI \ + -H "Authorization: Basic ${BASE64_CREDENTIALS}" | jq '.spec.upstream' +``` + +**Fix:** Ensure `upstream.main.url` is set to a valid backend service + +#### 5. Gateway-Controller Not Connected to APIM + +**Check if gateway is connected:** + +```bash +# Look for connection logs +export APIP_GW_CONTROLLER_LOGGING_LEVEL=debug +# Restart gateway controller +# Check logs for: "Bottom-up sync: starting" +``` + +**If not connected:** +1. Wait for automatic reconnection (happens periodically) +2. Or manually trigger by updating the API: + ```bash + curl -X PUT http://localhost:9090/api/management/v0.9/rest-apis/PetStoreAPI \ + -H "Content-Type: application/yaml" \ + -H "Authorization: Basic ${BASE64_CREDENTIALS}" \ + --data-binary @api-definition.yaml + ``` + +**View Gateway Connection Status:** + +Check gateway controller logs for: +- `"Connected to control plane"` - Connected to APIM +- `"Failed to resolve gateway path"` - Can't reach APIM +- `"Resolved WebSocket connect URL"` - On-prem mode detected + +### Issue: "Access Token Not Found" + +**Cause:** APIM OAuth2 token endpoint returned invalid response + +**Solution:** +1. Verify credentials are correct +2. Check APIM OAuth2 endpoint is accessible +3. Enable debug logging to see the actual response + +```bash +export APIP_GW_CONTROLLER_LOGGING_LEVEL=debug +``` + +### Issue: "On-prem Control Plane Mode Not Enabled" + +**Cause:** `IsOnPrem()` check is failing + +**Solution:** +1. Verify APIM `.well-known` endpoint returns `gatewayPath` +2. Check network connectivity to APIM +3. Verify `host` configuration is correct + +```bash +# Test well-known endpoint +curl -k https://192.168.0.102:9443/internal/gateway/.well-known +``` + +Expected response: +```json +{ + "gatewayPath": "/internal/data/v1" +} +``` + +--- diff --git a/en/docs/api-gateway/1.2.0/deployment/deployment-modes/immutable-gateway.md b/en/docs/api-gateway/1.2.0/deployment/deployment-modes/immutable-gateway.md new file mode 100644 index 000000000..e3e3fb846 --- /dev/null +++ b/en/docs/api-gateway/1.2.0/deployment/deployment-modes/immutable-gateway.md @@ -0,0 +1,171 @@ +--- +title: "Immutable Gateway" +description: "Run API Platform Gateway in immutable mode, loading API configurations from files at startup for GitOps and immutable infrastructure workflows." +canonical_url: https://wso2.com/api-platform/docs/api-gateway/deployment/deployment-modes/immutable-gateway/ +md_url: https://wso2.com/api-platform/docs/api-gateway/deployment/deployment-modes/immutable-gateway.md +tags: + - api-gateway + - deployment + - gitops +author: WSO2 API Platform Documentation Team +last_updated: 2026-06-11 +content_type: "how-to" +--- + +# Immutable Gateway (V4.0.0) + +This guide explains how to run the API Platform Gateway in **immutable mode**, where API configurations are loaded from files at startup instead of being managed through the REST API. + +Immutable Gateway is the latest evolution of the WSO2 Micro Gateway experience. +If you are coming from WSO2 Micro Gateway `3.2.x`, treat this as the successor track with enhanced API Platform integration, cloud-native deployment patterns, and improved artifact management workflows. + +- Previous generation (reference): [WSO2 Micro Gateway 3.2.x docs](https://mg.docs.wso2.com/en/latest/) +- Current generation: Immutable Gateway V4.0.0 (this documentation set) + +## Overview + +In immutable mode, the gateway controller reads API artifacts from a local directory at startup and applies them automatically. The management REST API remains available for read operations but rejects any mutating requests (`POST`, `PUT`, `PATCH`, `DELETE`) with `405 Method Not Allowed`. + +This is the recommended approach for: + +- **GitOps workflows** — store artifacts in a Git repository and bake them into a custom gateway image via CI/CD. +- **Immutable infrastructure** — ship a self-contained gateway image that requires no runtime configuration. +- **Kubernetes deployments** — mount a ConfigMap or Secret as a volume at the artifacts path. + +## Configuration + +Enable immutable mode in `config.toml`: + +```toml +[immutable_gateway] +enabled = true +``` + +By default, the gateway controller loads artifacts from `/etc/api-platform-gateway/immutable_gateway/artifacts`. You only need to set `artifacts_dir` if you want to use a different path. + +> **Note:** Full configuration reference with all options: +> +> ```toml +> [immutable_gateway] +> enabled = true +> artifacts_dir = "/etc/api-platform-gateway/immutable_gateway/artifacts" +> ``` + +!!! note "Toggling immutable mode from the environment" + Environment variables do not override `config.toml` keys directly — a value reaches a key only through an `env` interpolation token in the config file, and the shipped config carries no token for the `[immutable_gateway]` section. To drive this section from an environment variable, add the tokens to your `config.toml` first: + + {% raw %} + ```toml + [immutable_gateway] + enabled = '{{ env "APIP_GW_IMMUTABLE_GATEWAY_ENABLED" "false" }}' + artifacts_dir = '{{ env "APIP_GW_IMMUTABLE_GATEWAY_ARTIFACTS_DIR" "/etc/api-platform-gateway/immutable_gateway/artifacts" }}' + ``` + {% endraw %} + + then set `APIP_GW_IMMUTABLE_GATEWAY_ENABLED=true`. See [Gateway Configuration and Environment Interpolation](../../setup/configuration.md). + +## Artifact format + +Artifacts use the same Kubernetes-style resource format supported by the gateway. Each file must include `apiVersion`, `kind`, `metadata`, and `spec`. + +### Injecting environment variables + +Artifact files support Go template expressions for injecting dynamic values. Templates are rendered on the raw artifact string before YAML parsing, so expressions work in any string field — `upstream`, `auth`, policy `params`, etc. + +{% raw %} +| Function | Redacted in config dumps | Use for | +|---|---|---| +| `{{ env "KEY" }}` | No | Non-sensitive env vars (URLs, policy param values) | +| `{{ env "KEY" | redact }}` | Yes | Sensitive env vars (tokens, API keys) | +{% endraw %} + +Use `| redact` for sensitive values to hide them from config dumps. A `| default "value"` pipe is available for fallback values. See [Gateway Artifact Templating](../../setup/artifact-templating.md) for the full function reference. + +### Sample: Reading List API + +Save the following as `artifacts/reading-list-v1.yaml`: + +{% raw %} +```yaml +apiVersion: gateway.api-platform.wso2.com/v1alpha1 +kind: RestApi +metadata: + name: reading-list-api-v1 +spec: + displayName: Reading List API + version: v1.0 + context: /reading-list/$version + upstream: + main: + # read from env, falls back to the public sample backend if not set + url: '{{ env "BACKEND_URL" | default "https://apis.bijira.dev/samples/reading-list-api-service/v1.0" }}' + operations: + - method: GET + path: /books + - method: POST + path: /books + - method: GET + path: /books/{id} + - method: PUT + path: /books/{id} + - method: DELETE + path: /books/{id} +``` +{% endraw %} + +### Directory structure + +The controller walks all subdirectories, so you can organize artifacts however you like: + +``` +artifacts/ +├── rest-apis/ +│ ├── petstore-v1.yaml +│ └── orders-v2.yaml +├── llm-providers/ +│ └── openai.yaml +├── llm-proxies/ +│ └── chat-proxy.yaml +└── mcp-proxies/ + └── tools-proxy.yaml +``` + +## Deployment + +### Baking artifacts into a custom image + +The recommended GitOps approach is to build a custom gateway-controller image with artifacts copied in at build time: + +```dockerfile +FROM ghcr.io/wso2/api-platform/gateway-controller:1.0.0 + +COPY ./artifacts /etc/api-platform-gateway/immutable_gateway/artifacts + +# Bake in a config.toml that sets [immutable_gateway] enabled = true. +COPY ./config.toml /etc/gateway-controller/config.toml +``` + +### Kubernetes — mounting a ConfigMap volume + +You can also mount artifacts as a volume from a ConfigMap or Secret without building a custom image: + +```yaml +volumes: + - name: gateway-artifacts + configMap: + name: gateway-artifacts +volumeMounts: + - name: gateway-artifacts + mountPath: /etc/api-platform-gateway/immutable_gateway/artifacts + readOnly: true +``` + +Ensure the controller's mounted `config.toml` sets `[immutable_gateway] enabled = true` (see [Configuration](#configuration)). + +## Invoking the API + +Once the gateway is running with the sample artifact, invoke the Reading List API: + +```bash +curl -i http://localhost:8080/reading-list/v1.0/books +``` \ No newline at end of file diff --git a/en/docs/api-gateway/1.2.0/deployment/deployment-modes/kubernetes/gateway-operator-management-crds.md b/en/docs/api-gateway/1.2.0/deployment/deployment-modes/kubernetes/gateway-operator-management-crds.md new file mode 100644 index 000000000..67e6eda31 --- /dev/null +++ b/en/docs/api-gateway/1.2.0/deployment/deployment-modes/kubernetes/gateway-operator-management-crds.md @@ -0,0 +1,726 @@ +--- +title: "Gateway Operator Management CRDs" +description: "Reference for management-API-backed Kubernetes CRDs in API Platform Gateway: LlmProvider, LlmProxy, Mcp, ApiKey, SubscriptionPlan, Subscription, and Certificate." +canonical_url: https://wso2.com/api-platform/docs/api-gateway/deployment/deployment-modes/kubernetes/gateway-operator-management-crds/ +md_url: https://wso2.com/api-platform/docs/api-gateway/deployment/deployment-modes/kubernetes/gateway-operator-management-crds.md +tags: + - api-gateway + - kubernetes + - deployment + - operator +author: WSO2 API Platform Documentation Team +last_updated: 2026-06-17 +content_type: "reference" +--- + +# Gateway Operator — Management CRDs + +This document covers the optional management-API-backed custom resources supported by the Gateway Operator: `LlmProviderTemplate`, `LlmProvider`, `LlmProxy`, `Mcp`, `ApiKey`, `SubscriptionPlan`, `Subscription`, and `Certificate`. + +For the main operator modes (Platform CRDs and Kubernetes Gateway API), see [Kubernetes Operator deployment mode](./gateway-operator.md). + +## Overview + +In addition to `RestApi`, the operator supports management-API-backed CRDs such as `LlmProviderTemplate`, `LlmProvider`, `LlmProxy`, `Mcp`, `ApiKey`, `SubscriptionPlan`, `Subscription`, and `Certificate`. + +These resources use the same gateway selection model as `RestApi` (labels + `APIGateway.spec.apiSelector`). + +## Shared Prerequisites + +Create a Kubernetes Secret containing credentials and tokens used by policy parameters and resource configurations: + +```sh +kubectl apply -f - <<'EOF' +apiVersion: v1 +kind: Secret +metadata: + name: httproute-demo-policy-credentials +type: Opaque +stringData: + subscriptionKey: My-Key + subscriptionKeyResource: My-Key-Resource + apikey-restapi-value: demo-apikey-value-1234567890-abcdef + apikey-header-name: X-API-Key + apikey-llmprovider-value: demo-llmprovider-apikey-value + apikey-llmproxy-value: demo-llmproxy-apikey-value + llm-upstream-authorization: Bearer sk-test-key + subscription-token: demo-subscription-token-1234567890-abcdef +EOF + +kubectl get secret httproute-demo-policy-credentials +``` + +**Note:** The operator uses Kubernetes-native `valueFrom.secretKeyRef` references in policy parameters and resource configurations, allowing dynamic secret resolution at reconciliation time. This approach provides better security and follows Kubernetes best practices for secret management. + +### Deploy LLM Backend Mock (Prerequisites for LLM Flow) + +The LLM resources require a mock OpenAI-compatible backend (Prism + nginx HTTPS). The manifest deploys to `apigateway-demo` by default; override to `default` namespace with: + +```sh +curl -sL https://raw.githubusercontent.com/wso2/api-platform/refs/heads/main/kubernetes/helm/resources/apigateway-restapi-operator-demo/05b0-mock-openapi-https.yaml \ + | sed 's/namespace: apigateway-demo/namespace: default/g' \ + | kubectl apply -f - + +kubectl get deploy,svc -n default -l 'app.kubernetes.io/name in (mock-openai,mock-openai-https)' +``` + +Wait for both Deployments to be `Ready` before proceeding. + +### Deploy ApiKey Resources (Prerequisites) + +Apply `ApiKey` CRs up front. The LLM-parent ApiKeys (`demo-llmprovider-apikey`, `demo-llmproxy-apikey`) will retry reconciliation automatically once their parent `LlmProvider`/`LlmProxy` are created. + +```sh +kubectl apply -f - <<'EOF' +apiVersion: gateway.api-platform.wso2.com/v1alpha1 +kind: ApiKey +metadata: + name: demo-llmprovider-apikey + labels: + gateway.api-platform.wso2.com/restapi-target: wso2-crd +spec: + parentRef: + kind: LlmProvider + name: demo-llm-provider-apikey + displayName: demo key for llm provider parent + apiKey: + valueFrom: + name: httproute-demo-policy-credentials + key: apikey-llmprovider-value + expiresIn: + duration: 30 + unit: days +--- +apiVersion: gateway.api-platform.wso2.com/v1alpha1 +kind: ApiKey +metadata: + name: demo-llmproxy-apikey + labels: + gateway.api-platform.wso2.com/restapi-target: wso2-crd +spec: + parentRef: + kind: LlmProxy + name: demo-llm-proxy-apikey + displayName: demo key for llm proxy parent + apiKey: + valueFrom: + name: httproute-demo-policy-credentials + key: apikey-llmproxy-value + expiresIn: + duration: 30 + unit: days +EOF + +kubectl get apikey +``` + +## Deploy LLMProviderTemplate, LLMProvider, LLMProxy + +```sh +kubectl apply -f - <<'EOF' +apiVersion: gateway.api-platform.wso2.com/v1alpha1 +kind: LlmProviderTemplate +metadata: + name: openai-test + labels: + gateway.api-platform.wso2.com/restapi-target: wso2-crd +spec: + displayName: OpenAI + promptTokens: + location: payload + identifier: $.usage.prompt_tokens + completionTokens: + location: payload + identifier: $.usage.completion_tokens + totalTokens: + location: payload + identifier: $.usage.total_tokens + remainingTokens: + location: header + identifier: x-ratelimit-remaining-tokens + requestModel: + location: payload + identifier: $.model + responseModel: + location: payload + identifier: $.model +--- +apiVersion: gateway.api-platform.wso2.com/v1alpha1 +kind: LlmProvider +metadata: + name: demo-llm-provider-apikey + labels: + gateway.api-platform.wso2.com/restapi-target: wso2-crd +spec: + displayName: OpenAI Provider with APIKey + version: v1.0 + template: openai-test + context: /llm-invoke-context-apikey + accessControl: + mode: allow_all + policies: + - name: api-key-auth + version: v1 + paths: + - path: /chat/completions + methods: [POST] + params: + key: + valueFrom: + secretKeyRef: + name: httproute-demo-policy-credentials + key: apikey-header-name + in: header + upstream: + url: http://mock-openai:4010/openai/v1 + auth: + type: api-key + header: Authorization + value: + valueFrom: + name: httproute-demo-policy-credentials + key: llm-upstream-authorization +--- +apiVersion: gateway.api-platform.wso2.com/v1alpha1 +kind: LlmProxy +metadata: + name: demo-llm-proxy-apikey + labels: + gateway.api-platform.wso2.com/restapi-target: wso2-crd +spec: + displayName: Lifecycle LLM Proxy with APIKey + version: v1.0 + context: /proxy-invoke-test-apikey + policies: + - name: api-key-auth + version: v1 + paths: + - path: /chat/completions + methods: [POST] + params: + key: + valueFrom: + secretKeyRef: + name: httproute-demo-policy-credentials + key: apikey-header-name + in: header + provider: + id: demo-llm-provider-apikey + auth: + type: api-key + header: X-API-Key + value: + valueFrom: + name: httproute-demo-policy-credentials + key: apikey-llmprovider-value +EOF + +kubectl get llmprovidertemplate,llmprovider,llmproxy +``` + +**valueFrom pattern for LLM resources:** +- Policy parameters in `policies[].paths[].params` support `valueFrom.secretKeyRef` for dynamic secret resolution. +- Upstream authentication credentials can be sourced from Kubernetes Secrets via `valueFrom`. +- The operator resolves all `valueFrom` references before sending the configuration to the gateway. + +### Sample LLM API-key Protected Invocations + +```bash +# LlmProvider API-key protected (wrong key -> reject) +curl -sS -k -i \ + -H 'Content-Type: application/json' \ + -H 'X-API-Key: wrong-api-key-not-valid' \ + --request POST \ + --url 'https://localhost:8443/llm-invoke-context-apikey/chat/completions' \ + --data '{ + "model": "gpt-4", + "messages": [{"role":"user","content":"Hello from provider apikey test"}] + }' + +# LlmProvider API-key protected (valid key -> 200) +curl -sS -k \ + -H 'Content-Type: application/json' \ + -H 'X-API-Key: demo-llmprovider-apikey-value' \ + --request POST \ + --url 'https://localhost:8443/llm-invoke-context-apikey/chat/completions' \ + --data '{ + "model": "gpt-4", + "messages": [{"role":"user","content":"Hello from provider apikey test"}] + }' + +# LlmProxy API-key protected (valid key -> 200) +curl -sS -k \ + -H 'Content-Type: application/json' \ + -H 'X-API-Key: demo-llmproxy-apikey-value' \ + --request POST \ + --url 'https://localhost:8443/proxy-invoke-test-apikey/chat/completions' \ + --data '{ + "model": "gpt-4", + "messages": [{"role":"user","content":"Hello from proxy apikey test"}] + }' +``` + +## Deploy MCP + +### Deploy MCP Backend (Prerequisites for MCP Flow) + +The MCP resources require the `mcp-server-backend` service. The manifest deploys to `apigateway-demo` by default; override to `default` namespace with: + +```sh +curl -sL https://raw.githubusercontent.com/wso2/api-platform/refs/heads/main/kubernetes/helm/resources/apigateway-restapi-operator-demo/05c0-mcp-server-backend.yaml \ + | sed 's/namespace: apigateway-demo/namespace: default/g' \ + | kubectl apply -f - + +kubectl get deploy,svc -n default -l app.kubernetes.io/name=mcp-server-backend +``` + +Wait for the Deployment to be `Ready` before proceeding. + +### Deploy MCP CR + +```sh +kubectl apply -f - <<'EOF' +apiVersion: gateway.api-platform.wso2.com/v1alpha1 +kind: Mcp +metadata: + name: everything-mcp-v1.0 + labels: + gateway.api-platform.wso2.com/restapi-target: wso2-crd +spec: + displayName: Everything + version: v1.0 + context: /everything + specVersion: "2025-06-18" + upstream: + url: http://mcp-server-backend:3001 + tools: [] + resources: [] + prompts: [] +EOF + +kubectl get mcp +``` + +### Sample MCP Client Invocations + +```bash +# 1) MCP initialize (capture headers to extract mcp-session-id) +curl -sS -k -D /tmp/mcp-init-headers.txt \ + -H 'Content-Type: application/json' \ + -H 'Accept: application/json, text/event-stream' \ + --request POST \ + --url 'https://localhost:8443/everything/mcp' \ + --data '{ + "jsonrpc":"2.0", + "id":1, + "method":"initialize", + "params":{ + "protocolVersion":"2025-06-18", + "capabilities":{"roots":{"listChanged":true}}, + "clientInfo":{"name":"gateway-it-client","version":"1.0.0"} + } + }' + +# 2) Extract session id from initialize response headers +SESSION_ID="$(awk 'BEGIN{IGNORECASE=1} /^mcp-session-id:/ {print $2}' /tmp/mcp-init-headers.txt | tr -d '\r')" +echo "SESSION_ID=$SESSION_ID" + +# 3) MCP tools/call ("add") using same session +curl -sS -k \ + -H 'Content-Type: application/json' \ + -H 'Accept: application/json, text/event-stream' \ + -H "mcp-session-id: ${SESSION_ID}" \ + --request POST \ + --url 'https://localhost:8443/everything/mcp' \ + --data '{ + "jsonrpc":"2.0", + "id":2, + "method":"tools/call", + "params":{ + "name":"add", + "arguments":{"a":40,"b":60} + } + }' +``` + +Expected result: initialize returns success JSON-RPC response; `tools/call` returns `result.content[0].text` containing `The sum of 40 and 60 is 100.` + +## Deploy RestAPI with Policy Parameter from Secret + +```sh +kubectl apply -f - <<'EOF' +apiVersion: gateway.api-platform.wso2.com/v1alpha1 +kind: RestApi +metadata: + name: hello-normal-api-policy + labels: + gateway.api-platform.wso2.com/restapi-target: wso2-crd +spec: + displayName: hello-normal-api-policy + version: v1.0 + context: /hello-normal-policy + upstream: + main: + url: http://hello-backend:9080 + policies: + - name: subscription-validation + version: v1 + params: + subscriptionKeyHeader: + valueFrom: + secretKeyRef: + name: httproute-demo-policy-credentials + key: subscriptionKey + - name: set-headers + version: v1 + params: + request: + headers: + - name: X-Client-Version + value: "1.2.3" + operations: + - method: GET + path: /test-policy + - method: GET + path: /test-policy-resource + policies: + - name: subscription-validation + version: v1 + params: + subscriptionKeyHeader: + valueFrom: + secretKeyRef: + name: httproute-demo-policy-credentials + key: subscriptionKeyResource +EOF + +kubectl get restapi +``` + +Sample verification curls: + +```bash +curl --request GET \ + --url https://localhost:8443/hello-normal-policy/test-policy \ + --header 'Accept: application/json' -k + +curl --request GET \ + --url https://localhost:8443/hello-normal-policy/test-policy-resource \ + --header 'Accept: application/json' -k +``` + +**Pattern explanation:** +- Policy parameters support both inline values and `valueFrom` references. +- `valueFrom.secretKeyRef` resolves the value from a Kubernetes Secret at reconciliation time. +- Nested fields within policy parameters are automatically traversed and resolved. +- The resolved string value replaces the `valueFrom` object before the API configuration is sent to the gateway. + +## Deploy Hello Backend (Prerequisites for RestAPI Flows) + +The RestAPI resources route to the `hello-backend` service. The manifest deploys to `apigateway-demo` by default; override to `default` namespace with: + +```sh +curl -sL https://raw.githubusercontent.com/wso2/api-platform/refs/heads/main/kubernetes/helm/resources/apigateway-restapi-operator-demo/03-backend.yaml \ + | sed 's/namespace: apigateway-demo/namespace: default/g' \ + | kubectl apply -f - + +kubectl get deploy,svc -n default +``` + +Wait for the Deployment to be `Ready` before proceeding. + +## Deploy RESTAPI with API Key + +```sh +kubectl apply -f - <<'EOF' +apiVersion: gateway.api-platform.wso2.com/v1alpha1 +kind: RestApi +metadata: + name: hello-apikey-api + labels: + gateway.api-platform.wso2.com/restapi-target: wso2-crd +spec: + displayName: hello-apikey-api + version: v1.0 + context: /hello-apikey + upstream: + main: + url: http://hello-backend:9080 + policies: + - name: api-key-auth + version: v1 + params: + key: X-API-Key + in: header + operations: + - method: GET + path: /test +--- +apiVersion: gateway.api-platform.wso2.com/v1alpha1 +kind: ApiKey +metadata: + name: demo-restapi-apikey + labels: + gateway.api-platform.wso2.com/restapi-target: wso2-crd +spec: + parentRef: + kind: RestApi + name: hello-apikey-api + displayName: demo key for rest api parent + apiKey: + valueFrom: + name: httproute-demo-policy-credentials + key: apikey-restapi-value + expiresIn: + duration: 29 + unit: days +EOF + +kubectl get restapi,apikey +``` + +Sample verification curls: + +```sh +# Wrong key should be rejected (typically 401) +curl --request GET \ + --url https://localhost:8443/hello-apikey/test \ + --header 'Accept: application/json' \ + --header 'X-API-Key: wrong-api-key-not-valid' \ + -k -i + +# Correct key should pass (200) +curl --request GET \ + --url https://localhost:8443/hello-apikey/test \ + --header 'Accept: application/json' \ + --header 'X-API-Key: demo-apikey-value-1234567890-abcdef' \ + -k +``` + +## Deploy REST API with Subscriptions + +```sh +kubectl apply -f - <<'EOF' +apiVersion: gateway.api-platform.wso2.com/v1alpha1 +kind: RestApi +metadata: + name: hello-sub-api + labels: + gateway.api-platform.wso2.com/restapi-target: wso2-crd +spec: + displayName: hello-sub-api + version: v1.0 + context: /hello-sub + upstream: + main: + url: http://hello-backend:9080 + policies: + - name: subscription-validation + version: v1 + params: + subscriptionKeyHeader: + valueFrom: + secretKeyRef: + name: httproute-demo-policy-credentials + key: subscriptionKey + operations: + - method: GET + path: /new +--- +apiVersion: gateway.api-platform.wso2.com/v1alpha1 +kind: SubscriptionPlan +metadata: + name: demo-plan + labels: + gateway.api-platform.wso2.com/restapi-target: wso2-crd +spec: + planName: demo-plan + status: ACTIVE + stopOnQuotaReach: true + throttleLimitCount: 1000 + throttleLimitUnit: Min +--- +apiVersion: gateway.api-platform.wso2.com/v1alpha1 +kind: Subscription +metadata: + name: demo-subscription + labels: + gateway.api-platform.wso2.com/restapi-target: wso2-crd +spec: + apiId: hello-sub-api + subscriptionPlanId: demo-plan + status: ACTIVE + subscriptionToken: + valueFrom: + name: httproute-demo-policy-credentials + key: subscription-token +EOF + +kubectl get restapi,subscriptionplan,subscription +``` + +Sample verification curls: + +```sh +# Missing key should be rejected (403) +curl --request GET \ + --url https://localhost:8443/hello-sub/new \ + --header 'Accept: application/json' \ + -k -i + +# Wrong key should be rejected (403) +curl --request GET \ + --url https://localhost:8443/hello-sub/new \ + --header 'Accept: application/json' \ + --header 'My-Key: wrong-subscription-token-not-valid' \ + -k -i + +# Correct subscription token should pass (200) +curl --request GET \ + --url https://localhost:8443/hello-sub/new \ + --header 'Accept: application/json' \ + --header 'My-Key: demo-subscription-token-1234567890-abcdef' \ + -k +``` + +## Validation Notes + +- `Subscription.spec.subscriptionPlanId` can be a literal ID or a `SubscriptionPlan` CR name. +- `ApiKey.spec.parentRef.kind` supports `RestApi`, `LlmProvider`, and `LlmProxy`. +- `ApiKey.spec.expiresAt` and `ApiKey.spec.expiresIn` are mutually exclusive (CEL validation rejects both together). + +## valueFrom Pattern — Kubernetes-Native Secret Resolution + +The operator supports Kubernetes-native secret management through the `valueFrom` pattern, allowing policy parameters, credentials, and sensitive values to be resolved from Kubernetes Secrets at reconciliation time. + +### Supported Use Cases + +The `valueFrom` pattern is supported in: + +1. **Policy parameters** in RestApi, LlmProvider, and LlmProxy CRs + - Any policy param field can reference a Kubernetes Secret + - Nested policy parameter objects are recursively resolved + +2. **Upstream authentication** in LlmProvider and LlmProxy + - Bearer tokens and API keys sourced from Secrets + +3. **API Key values** in ApiKey CRs + - API key material resolved from Secrets + +4. **Subscription tokens** in Subscription CRs + - Authentication tokens resolved from Secrets + +### Pattern Syntax + +The `valueFrom` pattern supports two variants depending on the resource type: + +**Variant 1: Policy Parameters** (RestApi, LlmProvider, LlmProxy) + +Policy parameters use `valueFrom.secretKeyRef` for nested secret references: +```yaml +params: + subscriptionKeyHeader: + valueFrom: + secretKeyRef: + name: my-secret + key: subscription-key +``` + +**Variant 2: Direct Fields** (ApiKey, Subscription, Upstream Auth) + +Resource-level secrets use `valueFrom` directly with `name` and `key`: +```yaml +apiKey: + valueFrom: + name: my-secret + key: api-key-value +``` + +**Fields:** +- `name`: Required. Name of the Kubernetes Secret. +- `key`: Required. The data key within the Secret. +- `namespace`: Optional. Defaults to the CR's namespace. + +### How It Works + +1. Operator watches all Kubernetes `Secret` resources in the namespace. +2. At reconciliation, operator fetches referenced Secret and extracts the value. +3. The `valueFrom` object is replaced with the resolved string value. +4. Configuration is sent to gateway-controller with resolved values only. +5. If Secret is missing, reconciliation retries automatically. + +### Change Detection + +The operator computes a fingerprint of all referenced Secret `resourceVersion` fields. When a Secret changes, affected CRs are automatically re-reconciled, ensuring credentials stay synchronized without manual intervention. + +### Example: RestApi with Policy Parameter from Secret + +Create the Secret: +```yaml +apiVersion: v1 +kind: Secret +metadata: + name: api-credentials + namespace: default +type: Opaque +stringData: + subscription-key: "My-Subscription-Key-123" +``` + +Reference in RestApi: +```yaml +apiVersion: gateway.api-platform.wso2.com/v1alpha1 +kind: RestApi +metadata: + name: protected-api +spec: + context: /protected + upstream: + main: + url: https://backend.example.com + policies: + - name: subscription-validation + version: v1 + params: + subscriptionKeyHeader: + valueFrom: + secretKeyRef: + name: api-credentials + key: subscription-key +``` + +The gateway receives the resolved policy parameter: +```yaml +policies: + - name: subscription-validation + version: v1 + params: + subscriptionKeyHeader: "My-Subscription-Key-123" +``` + +Update the Secret anytime; the operator automatically redeploys with the new value. + +### Example 2: ApiKey with Direct valueFrom + +Reference in ApiKey (using `name` and `key` directly): +```yaml +apiVersion: gateway.api-platform.wso2.com/v1alpha1 +kind: ApiKey +metadata: + name: my-api-key +spec: + parentRef: + kind: RestApi + name: protected-api + apiKey: + valueFrom: + name: api-credentials + key: api-key-material + expiresIn: + duration: 30 + unit: days +``` + +**Key Difference:** +- RestApi policy params use nested `secretKeyRef` structure +- ApiKey, Subscription, and upstream auth use flat `name`/`key` under `valueFrom` diff --git a/en/docs/api-gateway/1.2.0/deployment/deployment-modes/kubernetes/gateway-operator.md b/en/docs/api-gateway/1.2.0/deployment/deployment-modes/kubernetes/gateway-operator.md new file mode 100644 index 000000000..be2222ea2 --- /dev/null +++ b/en/docs/api-gateway/1.2.0/deployment/deployment-modes/kubernetes/gateway-operator.md @@ -0,0 +1,854 @@ +--- +title: "Deploy with the Kubernetes Gateway Operator" +description: "Deploy API Platform Gateway on Kubernetes using the operator with APIGateway and RestApi CRDs, or using the standard Kubernetes Gateway API with HTTPRoute." +canonical_url: https://wso2.com/api-platform/docs/api-gateway/deployment/deployment-modes/kubernetes/gateway-operator/ +md_url: https://wso2.com/api-platform/docs/api-gateway/deployment/deployment-modes/kubernetes/gateway-operator.md +tags: + - api-gateway + - kubernetes + - deployment + - operator +author: WSO2 API Platform Documentation Team +last_updated: 2026-06-17 +content_type: "how-to" +--- + +# Kubernetes Operator for API Platform Gateway + +The WSO2 API Platform Gateway Operator enables native Kubernetes deployment using a GitOps-friendly, operator-based model. It manages the full lifecycle of API gateways and REST APIs. You can use **either** platform CRDs **or** the **Kubernetes Gateway API** on the same operator build. + +Use this mode when you need: + +- Operator-driven reconciliation and drift correction. +- GitOps-friendly CRD workflows. +- A unified control surface for both API Platform CRDs and Kubernetes Gateway API resources. + +For mode comparison and overall context, see [API Platform Kubernetes Gateway overview](./overview.md). + +## Overview + +### Path A — Platform CRDs (`APIGateway` + `RestApi`) + +| CRD | Purpose | +|-----|---------| +| `APIGateway` | Deploys and configures gateway infrastructure (controller, router, policy engine) | +| `RestApi` | Defines API routes, upstreams, and policies | + +The operator watches these CRs, runs Helm for the gateway runtime, and deploys APIs through gateway-controller’s management REST API. + +### Path B — Kubernetes Gateway API (`Gateway` + `HTTPRoute`) + +| Resource | Purpose | +|----------|---------| +| `GatewayClass` | Cluster-scoped class your `Gateway` references (`spec.gatewayClassName` must match the operator allowlist). | +| `Gateway` (`gateway.networking.k8s.io`) | Triggers the same Helm-based gateway deployment as `APIGateway`; controller endpoint is registered for discovery by routes. | +| `HTTPRoute` | Parents attach to a `Gateway`; `backendRefs` target a Kubernetes `Service`. The operator maps the route to `APIConfigData` and calls gateway-controller **`/api/management/v0.9/rest-apis`** (same outcome as `RestApi`, different user surface). | +| `APIPolicy` (optional) | Rule or API-level policies for Gateway API flows; same CRD as HTTPRoute policy demos in-repo. | + +**Hands-on walkthrough:** manifests are in **[Kubernetes Gateway API path](#kubernetes-gateway-api-path)** below. + +## Prerequisites + +- Kubernetes cluster (Docker Desktop, Kind, Minikube, OpenShift, etc.) +- `kubectl` installed +- `helm` v3+ +- `jq` (for JSON output) + +## Installation + +### 1. Install Cert-Manager + +The operator requires cert-manager for TLS certificate management: + +```sh +helm repo add jetstack https://charts.jetstack.io --force-update +helm repo update + +helm install cert-manager jetstack/cert-manager \ + --namespace cert-manager \ + --create-namespace \ + --set crds.enabled=true +``` + +### 2. Install Gateway Operator + +```sh +helm install my-gateway-operator oci://ghcr.io/wso2/api-platform/helm-charts/gateway-operator --version 0.8.0 --set image.tag=0.8.1 +``` + +## Deploying an API Gateway + +Create an `APIGateway` resource to bootstrap gateway components: + +```yaml +apiVersion: gateway.api-platform.wso2.com/v1alpha1 +kind: APIGateway +metadata: + name: cluster-gw +spec: + apiSelector: + scope: Cluster # Accepts APIs from any namespace + infrastructure: + labels: + environment: dev + team: platform + annotations: + prometheus.io/scrape: "true" + configRef: + name: custom-gateway-values # Optional: reference a ConfigMap with custom Helm values +``` + +Apply the sample APIGateway using the YAML above: + + + +```sh +kubectl apply -f - <<'EOF' +apiVersion: v1 +kind: ConfigMap +metadata: + name: custom-gateway-values +data: + # The operator expects this key to be named exactly `values.yaml`. + # These are *overrides* deep-merged into the operator's default gateway_values.yaml. + values.yaml: | + gateway: + gatewayRuntime: + service: + type: ClusterIP +--- +apiVersion: gateway.api-platform.wso2.com/v1alpha1 +kind: APIGateway +metadata: + name: cluster-gw +spec: + apiSelector: + scope: Cluster # Accepts APIs from any namespace + infrastructure: + labels: + environment: dev + team: platform + annotations: + prometheus.io/scrape: "true" + configRef: + name: custom-gateway-values # Optional: reference a ConfigMap with custom Helm values +EOF + +kubectl get apigateway -n default -o json | jq '.items[0].status' +``` + +## Deploying REST APIs + +Define APIs using the `RestApi` custom resource: + +```yaml +apiVersion: gateway.api-platform.wso2.com/v1alpha1 +kind: RestApi +metadata: + name: my-api + labels: + environment: "dev" +spec: + displayName: My API + version: v1.0 + context: /test + upstream: + main: + url: https://httpbin.org/anything + operations: + - method: GET + path: /info + - method: POST + path: /submit +``` + +Apply the sample RestApi using the YAML above: + +```sh +kubectl apply -f - <<'EOF' +apiVersion: gateway.api-platform.wso2.com/v1alpha1 +kind: RestApi +metadata: + name: my-api + labels: + environment: "dev" +spec: + displayName: My API + version: v1.0 + context: /test + upstream: + main: + url: https://httpbin.org/anything + operations: + - method: GET + path: /info + - method: POST + path: /submit +EOF + +kubectl get restapi -n default -o json | jq '.items[0].status' +``` + +Port-forward the gateway service + +```sh +kubectl port-forward svc/cluster-gw-gateway-gateway-runtime 8443:8443 +``` + +Invoke the deployed API + +```sh +curl --request GET \ + --url https://localhost:8443/test/info \ + --header 'Accept: application/json' -k +``` + +## Management CRDs (LLM, MCP, API Key, Subscription) + +For complete documentation on optional management-API-backed CRDs (`LlmProviderTemplate`, `LlmProvider`, `LlmProxy`, `Mcp`, `ApiKey`, `SubscriptionPlan`, `Subscription`, `Certificate`), see [gateway-operator-management-crds.md](./gateway-operator-management-crds.md). + +That guide covers: +- Kubernetes Secret prerequisites for credentials and tokens +- LlmProviderTemplate, LlmProvider, and LlmProxy deployments +- MCP (Model Context Protocol) resources +- RestAPI with policy parameters from secrets +- API Key management across RestApi, LlmProvider, and LlmProxy parents +- Subscription and SubscriptionPlan resources +- valueFrom pattern for Kubernetes-native secret resolution +- Complete sample invocations and test endpoints + +## Kubernetes Gateway API path + +Use this when you prefer standard Gateway API resources instead of `APIGateway` / `RestApi`. The manifests below match the **`gateway-api-demo`** demo in this repository (`kubernetes/helm/resources/gateway-api-operator-demo/`). Apply them **in order**, or concatenate and `kubectl apply -f -`. + +### What you need + +- **Gateway Operator** Helm install with RBAC for `gateway.networking.k8s.io` (included in the operator chart). +- **Gateway API CRDs** in the cluster (cloud add-on, another controller, or `--set gatewayApi.installStandardCRDs=true` on a greenfield cluster where no conflicting CRD owner exists). +- **`GatewayClass`** whose `metadata.name` is listed in **`gatewayApi.managedGatewayClassNames`** (default includes `wso2-api-platform`). +- **`spec.controllerName`** on the `GatewayClass` should match the operator (`gateway.api-platform.wso2.com/gateway-operator`) so the operator can set **`Accepted`** status on the class. +- **cert-manager** if you add **Certificate** / **Issuer** via per-Gateway Helm values (not included in the minimal YAMLs below; extend with a `ConfigMap` and **`gateway.api-platform.wso2.com/helm-values-configmap`** on the `Gateway` when needed). +- A **`Service`** backend referenced from **`HTTPRoute.spec.rules[].backendRefs`**. + +### 1. Namespace + +```sh +kubectl apply -f - <<'EOF' +apiVersion: v1 +kind: Namespace +metadata: + name: gateway-api-demo + labels: + app.kubernetes.io/part-of: gateway-api-operator-demo +EOF + +kubectl get namespace gateway-api-demo +``` + +### 2. GatewayClass + +```sh +kubectl apply -f - <<'EOF' +# GatewayClass must use controllerName matching the operator so the operator can set status.conditions[Accepted]. +apiVersion: gateway.networking.k8s.io/v1 +kind: GatewayClass +metadata: + name: wso2-api-platform +spec: + controllerName: gateway.api-platform.wso2.com/gateway-operator +EOF + +kubectl get gatewayclass wso2-api-platform +``` + +### 3. Gateway + +!!! warning "Pre-create the encryption key Secret" + At-rest encryption is mandatory and fail-closed. The operator installs the gateway into the **Gateway's own namespace**, so the AES-256 key Secret must exist in that namespace (`gateway-api-demo` below) before you apply the `Gateway`, and the per-Gateway values must enable it (`gateway.controller.encryptionKeys.enabled: true` with a `secretName`). Create it as shown in [Security Hardening → Encryption Keys](../../production-deployment/security-hardening.md#encryption-keys), using `-n gateway-api-demo`. + +```sh +kubectl apply -f - <<'EOF' +# Per-Gateway Helm values enabling at-rest encryption (mandatory, fail-closed). +# Referenced by the Gateway below via the helm-values-configmap annotation. +apiVersion: v1 +kind: ConfigMap +metadata: + name: platform-gw-values + namespace: gateway-api-demo +data: + # The operator loads ConfigMap.data["values.yaml"] and merges it into the gateway Helm values. + values.yaml: | + gateway: + controller: + encryptionKeys: + enabled: true + secretName: gateway-encryption-keys +--- +# Triggers the operator: Helm installs release named platform-gw-gateway, then registers the gateway-controller Service. +apiVersion: gateway.networking.k8s.io/v1 +kind: Gateway +metadata: + name: platform-gw + namespace: gateway-api-demo + annotations: + # Per-Gateway Helm overrides (here: mandatory at-rest encryption). ConfigMap key must be values.yaml. + gateway.api-platform.wso2.com/helm-values-configmap: platform-gw-values + # Prevent this Gateway from matching RestApi CRs intended for APIGateway (CRD mode) in mixed demos. + gateway.api-platform.wso2.com/api-selector: '{"scope":"LabelSelector","matchLabels":{"gateway.api-platform.wso2.com/restapi-target":"k8s"}}' + labels: + app.kubernetes.io/part-of: gateway-api-operator-demo +spec: + gatewayClassName: wso2-api-platform + infrastructure: + labels: + environment: dev + team: platform + annotations: + prometheus.io/scrape: "true" + listeners: + - name: http + port: 8080 + protocol: HTTP + allowedRoutes: + namespaces: + from: Same + - name: https + port: 8443 + protocol: HTTPS + allowedRoutes: + namespaces: + from: Same +EOF + +kubectl get gateway -n gateway-api-demo +``` + +### 4. Sample backend (Deployment + Service) + +```sh +kubectl apply -f - <<'EOF' +apiVersion: apps/v1 +kind: Deployment +metadata: + name: hello-backend + namespace: gateway-api-demo + labels: + app: hello-backend + app.kubernetes.io/part-of: gateway-api-operator-demo +spec: + replicas: 1 + selector: + matchLabels: + app: hello-backend + template: + metadata: + labels: + app: hello-backend + spec: + containers: + - name: sample-backend + image: ghcr.io/wso2/api-platform/sample-service:latest + args: + - "-addr" + - ":9080" + - "-pretty" + ports: + - name: http + containerPort: 9080 + resources: + requests: + cpu: 10m + memory: 32Mi +--- +apiVersion: v1 +kind: Service +metadata: + name: hello-backend + namespace: gateway-api-demo + labels: + app.kubernetes.io/part-of: gateway-api-operator-demo +spec: + type: ClusterIP + selector: + app: hello-backend + ports: + - name: http + port: 9080 + targetPort: 9080 +EOF + +kubectl get deploy,svc -n gateway-api-demo +``` + +Wait until the **Gateway** is **Programmed** and gateway workloads are **Ready**, then apply the HTTPRoute(s). + +### 5. HTTPRoute (`hello-api`) + +```sh +kubectl apply -f - <<'EOF' +# Operator maps this route to APIConfigData and calls gateway-controller /api/management/v0.9/rest-apis. +# Default REST handle is namespace-name: gateway-api-demo-hello-api (override with gateway.api-platform.wso2.com/api-handle). +apiVersion: gateway.networking.k8s.io/v1 +kind: HTTPRoute +metadata: + name: hello-api + namespace: gateway-api-demo + labels: + app.kubernetes.io/part-of: gateway-api-operator-demo + annotations: + gateway.api-platform.wso2.com/api-version: "v1.0" + gateway.api-platform.wso2.com/context: "/hello-context" + gateway.api-platform.wso2.com/display-name: "Hello API" +spec: + parentRefs: + - group: gateway.networking.k8s.io + kind: Gateway + name: platform-gw + namespace: gateway-api-demo + hostnames: + - demo.gateway-api.local + rules: + - matches: + # match.method is optional; if omitted, the operator emits GET, POST, PUT, PATCH, DELETE, HEAD, OPTIONS for this path. + - path: + type: PathPrefix + value: /hello + method: GET + backendRefs: + - group: "" + kind: Service + name: hello-backend + port: 9080 + weight: 1 +EOF + +kubectl get httproute hello-api -n gateway-api-demo +``` + +Test Curl: + +```sh +curl --request GET \ + --url 'https://localhost:8443/hello-context/hello' \ + --header 'Accept: application/json' \ + -k +``` + +### 6. Optional: second HTTPRoute (`hello-api-2`) + +```sh +kubectl apply -f - <<'EOF' +apiVersion: gateway.networking.k8s.io/v1 +kind: HTTPRoute +metadata: + name: hello-api-2 + namespace: gateway-api-demo + labels: + app.kubernetes.io/part-of: gateway-api-operator-demo + annotations: + gateway.api-platform.wso2.com/display-name: "Hello API 2" +spec: + parentRefs: + - group: gateway.networking.k8s.io + kind: Gateway + name: platform-gw + namespace: gateway-api-demo + hostnames: + - demo.gateway-api.local + rules: + - matches: + - path: + type: PathPrefix + value: /hello + backendRefs: + - group: "" + kind: Service + name: hello-backend + port: 9080 + weight: 1 +EOF + +kubectl get httproute -n gateway-api-demo +``` + +Test Curl: + +```sh +curl --request GET \ + --url 'https://localhost:8443/hello' \ + --header 'Accept: application/json' \ + -k +``` + +Verify: `kubectl get gateway,httproute -n gateway-api-demo`, wait for parent conditions on the HTTPRoute, then exercise the API (port-forward or in-cluster curl to **gateway-runtime** HTTPS as in **Testing APIs** below). + +### HTTPRoute annotations (payload metadata) + +Common annotations on `HTTPRoute` are copied into the **`api.yaml`** payload (for example **`gateway.api-platform.wso2.com/context`**, **`api-version`**, **`api-handle`**, **`display-name`**, **`project-id`**). If **`context`** is omitted or only whitespace, it defaults to **`/`**. If a rule **`match`** omits **`method`**, the operator emits all RestApi-supported verbs for that path: GET, POST, PUT, PATCH, DELETE, HEAD, OPTIONS. + +### Mixed clusters (`RestApi` + `Gateway`) + +If you run **both** `APIGateway`-selected **`RestApi`** resources and **Gateway API** routes, keep the **`gateway.api-platform.wso2.com/api-selector`** annotation on the **`Gateway`** (as in the YAML above) so this gateway does not select `RestApi` CRs meant for another `APIGateway`. + +### Test API Endpoints + +**Kubernetes Gateway API** — HTTPRoute **`hello-api`** from [above](#5-httproute-hello-api): API **`context`** `/hello-context`, route match path prefix **`/hello`** (hits Envoy HTTPS on the forwarded router port): + +```sh +curl --request GET \ + --url 'https://localhost:8443/hello-context/hello' \ + --header 'Accept: application/json' \ + -k +``` + +Use **`NS=gateway-api-demo`** in the port-forward snippet when testing that demo. The sample backend may respond with a short plain-text body (e.g. `hello from gateway api demo`) depending on chart and image version. + +### 7. HTTPRoute with Policies (APIPolicy CR) + +Attach policies to HTTPRoutes using the `APIPolicy` CR (`gateway.api-platform.wso2.com/v1alpha1`). Two attachment modes: + +- **API-level** — set `spec.targetRef` to the HTTPRoute; entries in `spec.policies` are merged into `APIConfigData.policies`. +- **Rule-scoped** — omit `spec.targetRef`; reference the `APIPolicy` from an HTTPRoute rule via `filters[].type: ExtensionRef`. + +#### 7.1 APIPolicy CRs (API-level + rule-scoped) + +```sh +kubectl apply -f - <<'EOF' +# API-level policy: targetRef → hello-apipolicy-demo HTTPRoute +apiVersion: gateway.api-platform.wso2.com/v1alpha1 +kind: APIPolicy +metadata: + name: httproute-demo-api-level + namespace: gateway-api-demo + labels: + app.kubernetes.io/part-of: gateway-api-httproute-policies-demo +spec: + targetRef: + group: gateway.networking.k8s.io + kind: HTTPRoute + name: hello-apipolicy-demo + policies: + - name: set-headers + version: v1 + params: + request: + headers: + - name: X-Client-Version + value: "1.2.3" +--- +# Rule-scoped policy: no targetRef; referenced from HTTPRoute rule via ExtensionRef +apiVersion: gateway.api-platform.wso2.com/v1alpha1 +kind: APIPolicy +metadata: + name: httproute-demo-rule-ratelimit + namespace: gateway-api-demo + labels: + app.kubernetes.io/part-of: gateway-api-httproute-policies-demo +spec: + policies: + - name: basic-ratelimit + version: v1 + params: + limits: + - requests: 3 + duration: 1m +EOF + +kubectl get apipolicy -n gateway-api-demo +``` + +#### 7.2 HTTPRoute with ExtensionRef + +```sh +kubectl apply -f - <<'EOF' +# HTTPRoute: API-level policies from APIPolicy with targetRef; one rule with rule-level ExtensionRef → APIPolicy, +# and one rule with no filters (API-level only, no resource/rule-scoped policies). +apiVersion: gateway.networking.k8s.io/v1 +kind: HTTPRoute +metadata: + name: hello-apipolicy-demo + namespace: gateway-api-demo + labels: + app.kubernetes.io/part-of: gateway-api-httproute-policies-demo + annotations: + gateway.api-platform.wso2.com/api-handle: gateway-api-demo-hello-apipolicy + gateway.api-platform.wso2.com/api-version: "v1.0" + gateway.api-platform.wso2.com/context: "/hello-policies-context" + gateway.api-platform.wso2.com/display-name: "Hello API policies via APIPolicy CR" + gateway.api-platform.wso2.com/project-id: "1234567890" +spec: + parentRefs: + - group: gateway.networking.k8s.io + kind: Gateway + name: platform-gw + namespace: gateway-api-demo + hostnames: + - demo.gateway-api.local + rules: + - matches: + - path: + type: PathPrefix + value: /hello-policies + method: GET + filters: + - type: ExtensionRef + extensionRef: + group: gateway.api-platform.wso2.com + kind: APIPolicy + name: httproute-demo-rule-ratelimit + backendRefs: + - group: "" + kind: Service + name: hello-backend + port: 9080 + weight: 1 + - matches: + - path: + type: PathPrefix + value: /hello-policies-plain + method: GET + backendRefs: + - group: "" + kind: Service + name: hello-backend + port: 9080 + weight: 1 +EOF + +kubectl get httproute hello-apipolicy-demo -n gateway-api-demo +``` + +Test the rule-scoped policy path (rate-limited) and the API-level-only path: + +```sh +curl --request GET \ + --url 'https://localhost:8443/hello-policies-context/hello-policies' \ + --header 'Accept: application/json' -k + +curl --request GET \ + --url 'https://localhost:8443/hello-policies-context/hello-policies-plain' \ + --header 'Accept: application/json' -k +``` + +#### 7.3 Secret-backed APIPolicy with `valueFrom` + +The operator resolves `params.valueFrom.secretKeyRef` (or `configMapKeyRef`) to a plain string before calling gateway-controller, and re-reconciles the HTTPRoute whenever the referenced Secret/ConfigMap changes. + +```sh +kubectl apply -f - <<'EOF' +# Secret + APIPolicy: subscription param resolved from Kubernetes Secret via valueFrom.secretKeyRef +apiVersion: v1 +kind: Secret +metadata: + name: httproute-demo-policy-credentials + namespace: gateway-api-demo + labels: + app.kubernetes.io/part-of: gateway-api-httproute-policies-demo +type: Opaque +stringData: + subscriptionKey: My-Key +--- +apiVersion: gateway.api-platform.wso2.com/v1alpha1 +kind: APIPolicy +metadata: + name: httproute-demo-rule-secret-params + namespace: gateway-api-demo + labels: + app.kubernetes.io/part-of: gateway-api-httproute-policies-demo +spec: + policies: + - name: subscription-validation + version: v1 + params: + subscriptionKeyHeader: + valueFrom: + secretKeyRef: + name: httproute-demo-policy-credentials + key: subscriptionKey + # namespace: +EOF + +kubectl get secret,apipolicy -n gateway-api-demo +``` + +#### 7.4 HTTPRoute referencing the secret-backed APIPolicy + +```sh +kubectl apply -f - <<'EOF' +# Second HTTPRoute: rule policy pulls sensitive param from Secret via APIPolicy params.valueFrom.secretKeyRef +apiVersion: gateway.networking.k8s.io/v1 +kind: HTTPRoute +metadata: + name: hello-apipolicy-secrets-demo + namespace: gateway-api-demo + labels: + app.kubernetes.io/part-of: gateway-api-httproute-policies-demo +spec: + parentRefs: + - group: gateway.networking.k8s.io + kind: Gateway + name: platform-gw + namespace: gateway-api-demo + hostnames: + - demo.gateway-api.local + rules: + - matches: + - path: + type: PathPrefix + value: /hello-secrets + method: GET + filters: + - type: ExtensionRef + extensionRef: + group: gateway.api-platform.wso2.com + kind: APIPolicy + name: httproute-demo-rule-secret-params + backendRefs: + - group: "" + kind: Service + name: hello-backend + port: 9080 + weight: 1 +EOF + +kubectl get httproute hello-apipolicy-secrets-demo -n gateway-api-demo +``` + +Test: + +```sh +curl --request GET \ + --url 'https://localhost:8443/hello-secrets' \ + --header 'Accept: application/json' -k +``` + +**Validate Secret watch:** patch the Secret and confirm the operator re-reconciles `hello-apipolicy-secrets-demo` without editing the HTTPRoute: + +```sh +kubectl patch secret httproute-demo-policy-credentials -n gateway-api-demo \ + --type merge -p '{"stringData":{"subscriptionKey":"Updated-Key"}}' +``` + + +## Adding Backend Certificates + +For APIs connecting to backends with self-signed certificates: + +### 1. Download the Certificate + +```sh +curl -X GET "https://raw.githubusercontent.com/wso2/api-platform/refs/heads/main/gateway/resources/secure-backend/test-backend-certs/test-backend.crt" \ + -o /tmp/test-backend.crt +``` + +### 2. Add Certificate to Gateway + +The management API uses basic auth with the credentials from your Helm values +(`controller.auth.basic.users`; the chart default is `admin` / `admin`). Export them, changing them if +you overrode the chart defaults: + +```sh +export ADMIN_USERNAME=admin +export ADMIN_PASSWORD=admin +``` + +```sh +cert_path="/tmp/test-backend.crt" +curl -X POST http://localhost:9090/api/management/v0.9/certificates -u "$ADMIN_USERNAME:$ADMIN_PASSWORD" \ + -H "Content-Type: application/json" \ + -d "{\"certificate\":$(jq -Rs . < $cert_path),\"filename\":\"my-cert.pem\", \"name\":\"test\"}" +``` + +## Custom Configuration + +Per-gateway Helm values are supplied as a **ConfigMap** whose data includes **`values.yaml`** (partial YAML is fine; the operator **deep-merges** it onto the operator’s default gateway values file loaded from **`gateway.helm.valuesFilePath`**). + +### `APIGateway` (`spec.configRef`) + +Create the ConfigMap: + +```yaml +apiVersion: v1 +kind: ConfigMap +metadata: + name: gateway-custom-config +data: + values.yaml: | + ### IMPORTANT you need to provide entire values yaml of the gateway helm. + gateway: + controller: + logging: + level: debug + router: + service: + type: LoadBalancer +``` + +Reference it from the **APIGateway**: + +```yaml +spec: + configRef: + name: gateway-custom-config +``` + +### Kubernetes Gateway API (`Gateway`) + +Use the **same ConfigMap** shape (`data.values.yaml`). Put the ConfigMap in the **same namespace** as the **`Gateway`**, then point the **`Gateway`** at it with this annotation (not a field on **`spec`**): + +```yaml +apiVersion: gateway.networking.k8s.io/v1 +kind: Gateway +metadata: + name: platform-gw + namespace: gateway-api-demo + annotations: + gateway.api-platform.wso2.com/helm-values-configmap: gateway-custom-config + # ... other annotations (e.g. api-selector) as needed +spec: + gatewayClassName: wso2-api-platform + # listeners, infrastructure, ... +``` + +The operator reads **`metadata.annotations[gateway.api-platform.wso2.com/helm-values-configmap]`**, loads **`ConfigMap.data["values.yaml"]`**, and merges it into the Helm values used for **`{metadata.name}-gateway`**, same merge rules as **`APIGateway.spec.configRef`**. + +## Architecture + +``` +┌─────────────────────────────────────────────────────────────────┐ +│ Gateway Operator │ +│ Watches: APIGateway, RestApi; Gateway, HTTPRoute (+ Service, │ +│ APIPolicy, Secret, ConfigMap for Gateway API path) │ +└─────────────────────────────────────────────────────────────────┘ + │ + ▼ +┌─────────────────────────────────────────────────────────────────┐ +│ Gateway Components │ +│ ┌─────────────────┐ ┌────────┐ ┌──────────────────┐ │ +│ │ Gateway │ │ Router │ │ Policy Engine │ │ +│ │ Controller │ │(Envoy) │ │ │ │ +│ │ (Control Plane) │ │ │ │ │ │ +│ └─────────────────┘ └────────┘ └──────────────────┘ │ +└─────────────────────────────────────────────────────────────────┘ +``` + +- **CRD path:** `APIGateway` drives Helm; `RestApi` drives management REST deploys. +- **Gateway API path:** `Gateway` drives the same Helm install pattern; `HTTPRoute` is translated to the same management REST payload shape as `RestApi`. + +## Default Ports + +| Port | Component | Description | +|------|-----------|-------------| +| 9090 | Controller | REST API for management | +| 18000 | Controller | xDS gRPC for Envoy | +| 18001 | Controller | Policy xDS | +| 8080 | Router | HTTP traffic | +| 8443 | Router | HTTPS traffic | +| 9901 | Router | Envoy admin | +| 9001 | Policy Engine | ext_proc gRPC | + +## See Also + +- [Gateway Quick Start (Docker Compose)](../../../quick-start-guide.md) +- [Policies](../../../overview.md#policies) +- [Gateway Controller Management API](../../../gateway-controller-management-api/overview.md) \ No newline at end of file diff --git a/en/docs/api-gateway/1.2.0/deployment/deployment-modes/kubernetes/kubernetes-standalone.md b/en/docs/api-gateway/1.2.0/deployment/deployment-modes/kubernetes/kubernetes-standalone.md new file mode 100644 index 000000000..3194f45e4 --- /dev/null +++ b/en/docs/api-gateway/1.2.0/deployment/deployment-modes/kubernetes/kubernetes-standalone.md @@ -0,0 +1,310 @@ +--- +title: "Deploy in Kubernetes Standalone Mode" +description: "Install and manage API Platform Gateway on Kubernetes using the standalone Helm chart, without the Gateway Operator." +canonical_url: https://wso2.com/api-platform/docs/api-gateway/deployment/deployment-modes/kubernetes/kubernetes-standalone/ +md_url: https://wso2.com/api-platform/docs/api-gateway/deployment/deployment-modes/kubernetes/kubernetes-standalone.md +tags: + - api-gateway + - kubernetes + - deployment + - helm +author: WSO2 API Platform Documentation Team +last_updated: 2026-06-17 +content_type: "how-to" +--- + +# API Platform Gateway - Kubernetes Standalone Mode + +This guide explains how to run API Platform Gateway in **Standalone Mode** using the gateway Helm chart only (without the Gateway Operator). + +Standalone mode is recommended when you want: + +- Direct Helm-based lifecycle management of gateway components. +- A simpler footprint without operator-managed CRDs. +- Explicit control over values and release upgrades. + +For mode selection and architecture context, see [API Platform Kubernetes Gateway deployment modes](./overview.md). + +## What Gets Deployed + +The gateway chart deploys the runtime components used by API Platform Gateway (controller and gateway runtime workloads) from chart templates and values. + +Chart reference: + +- OCI chart: `oci://ghcr.io/wso2/api-platform/helm-charts/gateway` +- Local chart (repo): `kubernetes/helm/gateway-helm-chart` + +## Prerequisites + +- Kubernetes `1.24+` +- Helm `3.12+` +- `kubectl` +- `cert-manager` (required for cert-manager-backed TLS flows) + +## Install cert-manager + +```bash +helm repo add jetstack https://charts.jetstack.io --force-update +helm repo update + +helm install cert-manager jetstack/cert-manager \ + --namespace cert-manager \ + --create-namespace \ + --set crds.enabled=true +``` + +Verify: + +```bash +kubectl get pods -n cert-manager +``` + +## Create the Encryption Key Secret + +At-rest encryption is **mandatory and fail-closed**. The chart refuses to render without an AES-256 key Secret. Create it in the namespace you install into, **before** installing the chart. + +```bash +openssl rand 32 > default-aesgcm256-v1.bin +kubectl create secret generic gateway-encryption-keys \ + --from-file=default-aesgcm256-v1.bin=default-aesgcm256-v1.bin && \ + rm default-aesgcm256-v1.bin # remove the plaintext key only after the Secret is created +# For a non-default namespace, first `kubectl create namespace `, +# then add `-n ` to both this command and `helm install`. +``` + +The Secret's key entry must be named `default-aesgcm256-v1.bin`. See [Security Hardening → Encryption Keys](../../production-deployment/security-hardening.md#encryption-keys) for key rotation and multi-key setups. + +## Install Gateway Chart + +Use one of the following patterns. + +### Default install + +```bash +helm install ap-gateway oci://ghcr.io/wso2/api-platform/helm-charts/gateway \ + --set gateway.controller.encryptionKeys.enabled=true \ + --set gateway.controller.encryptionKeys.secretName=gateway-encryption-keys +``` + +### Install into a dedicated namespace + +```bash +kubectl create namespace api-gateway + +helm install ap-gateway oci://ghcr.io/wso2/api-platform/helm-charts/gateway \ + --namespace api-gateway \ + --set gateway.controller.encryptionKeys.enabled=true \ + --set gateway.controller.encryptionKeys.secretName=gateway-encryption-keys +``` + +### Install with control-plane overrides + +```bash +helm install ap-gateway oci://ghcr.io/wso2/api-platform/helm-charts/gateway \ + --set gateway.controller.controlPlane.host="platform.example.com" \ + --set gateway.controller.controlPlane.port=8443 \ + --set gateway.controller.controlPlane.token.value="your-token-here" \ + --set gateway.controller.encryptionKeys.enabled=true \ + --set gateway.controller.encryptionKeys.secretName=gateway-encryption-keys +``` + +### Install with a values file + +`custom-values.yaml` must define the mandatory encryption settings: + +```yaml +gateway: + controller: + encryptionKeys: + enabled: true + secretName: gateway-encryption-keys +``` + +```bash +helm install ap-gateway oci://ghcr.io/wso2/api-platform/helm-charts/gateway \ + -f custom-values.yaml +``` + +## Verify Installation + +```bash +helm status ap-gateway +kubectl get all -l app.kubernetes.io/instance=ap-gateway +``` + +Check logs: + +```bash +# Controller logs +kubectl logs -l app.kubernetes.io/component=controller + +# Gateway runtime logs +kubectl logs -l app.kubernetes.io/component=gateway-runtime +``` + +## Upgrade and Uninstall + +Upgrade: + +```bash +helm upgrade ap-gateway oci://ghcr.io/wso2/api-platform/helm-charts/gateway -f custom-values.yaml +``` + +Uninstall: + +```bash +helm uninstall ap-gateway +``` + +Namespace-scoped uninstall: + +```bash +helm uninstall ap-gateway --namespace api-gateway +``` + +## Core Configuration Areas + +Most runtime configuration is controlled in `values.yaml`. Common sections: + +- `gateway.controller.image`, `gateway.gatewayRuntime.image` +- `gateway..deployment.*` +- `gateway..service.*` +- `gateway.controller.controlPlane.*` +- `gateway.controller.logging.*` +- `gateway.controller.tls.*` +- `gateway.controller.upstreamCerts.*` +- `gateway.config.policy_engine.*` + +Refer to inline comments in chart `values.yaml` for all supported fields. + +## TLS Configuration + +### Option 1: cert-manager (recommended) + +```bash +helm install ap-gateway oci://ghcr.io/wso2/api-platform/helm-charts/gateway \ + --set gateway.controller.tls.enabled=true +``` + +Production-style example: + +```yaml +gateway: + controller: + tls: + enabled: true + certificateProvider: cert-manager + certManager: + createIssuer: false + issuerRef: + name: letsencrypt-prod + kind: Issuer + commonName: api.example.com + dnsNames: + - api.example.com + - "*.api.example.com" +``` + +### Option 2: Existing TLS secret + +```bash +kubectl create secret tls gateway-tls \ + --cert=path/to/tls.crt \ + --key=path/to/tls.key + +helm install ap-gateway oci://ghcr.io/wso2/api-platform/helm-charts/gateway \ + --set gateway.controller.tls.enabled=true \ + --set gateway.controller.tls.certificateProvider=secret \ + --set gateway.controller.tls.secret.name=gateway-tls +``` + +## Upstream Custom CAs + +When calling upstream services that use private/self-signed CAs: + +```bash +kubectl create secret generic upstream-ca-certs \ + --from-file=ca1.crt=path/to/ca1.crt \ + --from-file=ca2.crt=path/to/ca2.crt + +helm install ap-gateway oci://ghcr.io/wso2/api-platform/helm-charts/gateway \ + --set gateway.controller.upstreamCerts.enabled=true \ + --set gateway.controller.upstreamCerts.secretName=upstream-ca-certs +``` + +## Create and Invoke API + +### Port-forward Gateway Controller Service + +```bash +kubectl port-forward svc/ap-gateway-controller 9090:9090 +``` + +### Verify gateway controller admin endpoint is running +```bash +curl http://localhost:9094/api/admin/v0.9/health +``` + +### Deploy an API configuration + +The management API uses basic auth with the credentials from your Helm values +(`controller.auth.basic.users`; the chart default is `admin` / `admin`). Export them, changing them if +you overrode the chart defaults: + +```bash +export ADMIN_USERNAME=admin +export ADMIN_PASSWORD=admin +``` + +```bash +curl -X POST http://localhost:9090/api/management/v0.9/rest-apis \ + -u "$ADMIN_USERNAME:$ADMIN_PASSWORD" \ + -H "Content-Type: application/yaml" \ + --data-binary @- <<'EOF' +apiVersion: gateway.api-platform.wso2.com/v1alpha1 +kind: RestApi +metadata: + name: reading-list-api-v1.0 +spec: + displayName: Reading-List-API + version: v1.0 + context: /reading-list/$version + upstream: + main: + url: https://apis.bijira.dev/samples/reading-list-api-service/v1.0 + policies: + - name: set-headers + version: v1 + params: + request: + headers: + - name: x-wso2-apip-gateway-version + value: v1.0.0 + response: + headers: + - name: x-environment + value: development + operations: + - method: GET + path: /books + - method: POST + path: /books + - method: GET + path: /books/{id} + - method: PUT + path: /books/{id} + - method: DELETE + path: /books/{id} +EOF +``` + +### Test routing through the gateway +```bash +curl http://localhost:8080/reading-list/v1.0/books +curl -k https://localhost:8443/reading-list/v1.0/books +``` + +## Next Steps + +- For operator-managed lifecycle and CRDs, see [Kubernetes Operator deployment mode](./gateway-operator.md). +- For mode comparison and migration context from Kubernetes Gateway 1.3.0, see the [deployment modes overview page](./overview.md). \ No newline at end of file diff --git a/en/docs/api-gateway/1.2.0/deployment/deployment-modes/kubernetes/overview.md b/en/docs/api-gateway/1.2.0/deployment/deployment-modes/kubernetes/overview.md new file mode 100644 index 000000000..0527f1bf9 --- /dev/null +++ b/en/docs/api-gateway/1.2.0/deployment/deployment-modes/kubernetes/overview.md @@ -0,0 +1,104 @@ +--- +title: "API Platform Kubernetes Gateway (V2.0.0)" +description: "Overview of API Platform Kubernetes Gateway V2.0.0 deployment modes: standalone, operator-managed, and Kubernetes Gateway API workflows." +canonical_url: https://wso2.com/api-platform/docs/api-gateway/deployment/deployment-modes/kubernetes/overview/ +md_url: https://wso2.com/api-platform/docs/api-gateway/deployment/deployment-modes/kubernetes/overview.md +tags: + - api-gateway + - kubernetes + - deployment +author: WSO2 API Platform Documentation Team +last_updated: 2026-06-19 +content_type: "overview" +--- + +# API Platform Kubernetes Gateway (V2.0.0) + +API Platform Gateway is the latest evolution of the WSO2 Kubernetes gateway experience. +If you are coming from WSO2 Kubernetes gateway `1.3.0`, treat this as the successor track with a broader API Platform integration model and improved Kubernetes-native workflows. + +- Previous generation (reference): [WSO2 APK 1.3.0 docs](https://apk.docs.wso2.com/en/latest/) +- Current generation: API Platform Kubernetes Gateway V2.0.0 (this documentation set) + +## What Is New in V2.0.0 + +- Unified API Platform gateway model for both standalone and operator-driven deployments. +- Native support for Kubernetes Operator workflows and GitOps-style resource management. +- Flexible API exposure model: + - API Platform CRDs (`APIGateway`, `RestApi`, and related CRDs) + - Kubernetes Gateway API (`GatewayClass`, `Gateway`, `HTTPRoute`, optional `APIPolicy`) +- Improved extensibility for policies and secret/config-based parameter references. + +## Benefits of API Platform Kubernetes Gateway V2.0.0 + +### Unified API Platform Model + +- Single gateway model covering both standalone and operator-driven deployments, removing the fragmentation from the APK 1.3.0 era. + +### Kubernetes-Native Workflows + +- Native Kubernetes Operator support with declarative CRDs (`APIGateway`, `RestApi`, `Gateway`, `HTTPRoute`). + +### Flexible API Exposure Model + +- Supports both WSO2-native CRDs and the standard Kubernetes Gateway API, avoiding vendor lock-in. + +### Extensible Policy Engine + +- Policies can be attached to APIs in either deployment mode. +- Supports authentication, rate limiting, header manipulation, and custom request/response mediation. +- Dual-language policy development: Go for performance-critical logic, Python for AI/ML and data transformations. + +### High Availability and Production-Ready + +- Controller uses an external database (PostgreSQL or SQL Server) for persistent, shared API configuration storage. +- Runtime uses Redis for distributed rate limiting across replicas. + +### Built-In Observability + +- Metrics (Prometheus/Grafana), distributed tracing (Jaeger), logging, and Moesif analytics integration. + +### Improved Extensibility via Custom Policies and Secrets + +- Custom gateway images built with the `ap` CLI. +- Secrets and config-based parameter references for improved security posture. + +## Deployment Modes + +Choose one of the following modes based on your operational model. + +### 1) Kubernetes Standalone Mode + +Use this mode when you only need the gateway runtime and manage it directly via the gateway Helm chart. + +- Installs gateway components without the gateway operator. +- Best for simpler setups or environments where operator lifecycle management is not required. +- Guide: [`kubernetes-standalone.md`](./kubernetes-standalone.md) + +### 2) Kubernetes Operator Mode + +Use this mode when you want operator-managed lifecycle, declarative resources, and automation. + +- Installs the Gateway Operator via Helm. +- Supports: + - API Platform CRD path (`APIGateway` + `RestApi`) + - Kubernetes Gateway API path (`Gateway` + `HTTPRoute`) +- Guide: [`gateway-operator.md`](./gateway-operator.md) + +## How to Choose a Mode + +- Choose **Standalone Mode** if your priority is a lightweight gateway runtime install with Helm-managed values. +- Choose **Operator Mode** if your priority is Kubernetes-native reconciliation, GitOps workflows, multi-resource lifecycle automation, and CRD-based API management. + +## Policy Configuration + +API Platform Kubernetes Gateway V2.0.0 supports attaching policies to APIs deployed in either mode. Policies control request/response mediation, rate limiting, authentication, and other gateway behaviors. + +For a full reference on available policies and how to configure them, see [Policy Configuration Overview](https://wso2.com/api-platform/docs/api-gateway/policies/overview/). + +## Next Steps + +Learn more about the deployment modes: + +- [Standalone mode](./kubernetes-standalone.md) +- [Kubernetes Operator mode](./gateway-operator.md) diff --git a/en/docs/api-gateway/1.2.0/deployment/high-availability-production-deployment.md b/en/docs/api-gateway/1.2.0/deployment/high-availability-production-deployment.md new file mode 100644 index 000000000..2c5ed084a --- /dev/null +++ b/en/docs/api-gateway/1.2.0/deployment/high-availability-production-deployment.md @@ -0,0 +1,193 @@ +--- +title: "High-Availability Production Deployment" +description: "Deploy API Platform Gateway in a highly available, production-grade configuration on Kubernetes with Helm, an external database (PostgreSQL or SQL Server), and replicated workloads." +canonical_url: https://wso2.com/api-platform/docs/api-gateway/deployment/high-availability-production-deployment/ +md_url: https://wso2.com/api-platform/docs/api-gateway/deployment/high-availability-production-deployment.md +tags: + - api-gateway + - deployment + - kubernetes + - devops +author: WSO2 API Platform Documentation Team +last_updated: 2026-06-26 +content_type: "how-to" +--- + +# High-Availability Production Deployment + +This guide covers deploying the API Platform Gateway in a production-grade, highly available configuration using Helm on Kubernetes. Security is hardened, an external database (PostgreSQL or SQL Server) backs the deployment state, and workloads are replicated across nodes. + +## Prerequisites + +!!! warning "At-rest encryption is mandatory" + At-rest encryption is always required, and the chart is **fail-closed** — it refuses to render unless you provision an AES-256 key Secret and enable it (`gateway.controller.encryptionKeys.enabled: true` with a `secretName`). Provision the key before installing — see [Security Hardening → Encryption Keys](./production-deployment/security-hardening.md#encryption-keys). + +Ensure the following tools are installed and configured before starting: + +| Tool | Requirement | +|------|-------------| +| `kubectl` | Configured against your target cluster | +| `helm` | Version 3+ | +| `openssl` | Available in your local shell | + +Verify your environment: + +```bash +kubectl cluster-info +kubectl get nodes +helm version +``` + +## Cluster Topology + +Use at least two worker nodes for high availability. The recommended minimum production topology separates system and gateway workloads into dedicated node pools: + +| Node Pool | Purpose | Recommended Size | +|-----------|---------|-----------------| +| `systempool` | Kubernetes system workloads | 1–2 nodes | +| `gatewaypool` | Gateway runtime + controller | Minimum 2 nodes | + +This separation provides: + +- No single-node failure causes a full outage +- Safer autoscaling without disrupting system pods +- Improved workload isolation and resource predictability + +## Architecture + +The self-hosted gateway can be deployed in a highly available manner by running multiple **Gateway Controller** replicas and multiple **Gateway Runtime** replicas across different network zones or environments. + +In this deployment model, API deployments are received by one of the Gateway Controller replicas. The controller persists the API deployment information in the shared database. Other Gateway Controller replicas then read the updated deployment state from the database and synchronize the relevant configuration with the Gateway Runtime instances connected to them. + +This ensures that all Gateway Controller replicas operate with a consistent deployment state and that each runtime environment receives the latest API configuration. + +![High-availability setup example](../../../assets/img/api-platform-gateway/gateway/high-availability-architecture.png) + +### Architecture Overview + +The deployment consists of the following main components: + +| Component | Description | +| ----- | ----- | +| **Gateway Controller** | Receives API deployment requests, stores deployment state in the database, and synchronizes runtime configuration with connected Gateway Runtime instances. | +| **Database (PostgreSQL / SQL Server)** | Acts as the shared source of truth for API metadata, deployment state, and gateway configuration. | +| **Gateway Runtime** | Receives configuration from its connected Gateway Controller and enforces API gateway policies at runtime. | + +### Deployment Synchronization Flow + +When an API deployment request is received, it is handled by one of the available Gateway Controller replicas. + +The controller that receives the request validates the deployment and stores the API metadata and deployment state in the shared database. This database acts as the common source of truth for all Gateway Controller replicas. + +Other Gateway Controller replicas continuously read or synchronize the latest deployment state from the database. Once a controller detects a new or updated API deployment, it generates the required runtime configuration and synchronizes it with the Gateway Runtime instances connected to that controller. + +Each Gateway Runtime then applies the received configuration and starts serving the deployed APIs. + +Each Gateway Controller replica can manage one or more Gateway Runtime replicas. + +### High Availability Behavior + +High availability is achieved by removing dependency on a single controller instance. + +If an API deployment request is received by **Gateway Controller Replica 01**, that replica stores the deployment state in the shared database. **Gateway Controller Replica 02** can then read the same deployment state from the database and synchronize it with the Gateway Runtime replicas connected to it. + +![High-availability behavior](../../../assets/img/api-gateway/high-availability-deployment-example.png) + +If one Gateway Controller replica becomes unavailable, another replica can continue to process deployment requests and synchronize runtime configuration based on the state stored in the database. + +Similarly, multiple Gateway Runtime replicas can be deployed in each environment to ensure API traffic continues to be served even if one runtime replica becomes unavailable. + +### Configuration Synchronization + +The shared database is the central synchronization point between Gateway Controller replicas. It maintains the latest API deployment state and allows all controller replicas to operate consistently. + +Gateway Runtime replicas do not directly read from the database. Instead, they receive the required runtime configuration from their connected Gateway Controller. This keeps the runtime layer lightweight and allows the controller layer to manage configuration generation and synchronization. + +### Ingress Configuration + +The Helm chart deploys Kubernetes `Service` objects for the Gateway Runtime and the Controller REST API, but does not provision an Ingress Controller or Ingress resources. You are responsible for configuring external access to these services using the ingress solution of your choice. + +At a minimum, expose the Gateway Runtime service on port **8443** (HTTPS) to route inbound API traffic. + +If you are using the [bottom-up deployment](./deploying-apis/bottom-up-api-deployment.md) approach or running the gateway in standalone mode, also expose the Controller REST API service on port **9090** so that developers can deploy APIs directly to the gateway. + +## Before You Begin + +### Start with the Base Values File + +Download the default `values.yaml` for the gateway Helm chart and use it as the starting point for your production configuration. All steps in this guide reference fields within this file. + +```bash +curl -o values.yaml https://raw.githubusercontent.com/wso2/api-platform/refs/tags/gateway/v1.1.0/kubernetes/helm/gateway-helm-chart/values.yaml +``` + +### Pin the Image Versions + +Before making any other changes, set the controller and runtime image tags to the release you want to deploy. The latest stable release is **1.1.0**. + +```yaml +gateway: + controller: + image: + tag: "1.1.0" + gatewayRuntime: + image: + tag: "1.1.0" +``` + +!!! tip + Check the [gateway releases](https://github.com/wso2/api-platform/releases) page for the latest available version. Always ensure that the major version of the image tag matches the major version of the Helm chart used during installation. + +!!! note + It is strongly recommended to use the same version tag for both the controller and runtime components to ensure compatibility and avoid unexpected behavior. + +!!! note "WSO2 Subscription Users" + If you have a WSO2 subscription, use image tags that include the **U2 update version (4th digit)**, for example `1.1.0.1`, instead of the base release `1.1.0`. + + The 4th digit represents **patch-level (U2) updates**, which include the latest fixes and security updates delivered through the WSO2 private registry. See [WSO2 Subscription Users](#wso2-subscription-users) below for registry configuration. + +### WSO2 Subscription Users + +If you have a WSO2 Subscription, images are pulled from the WSO2 private registry (`registry.wso2.com`) instead of the public GHCR registry. A single Helm field activates this mode end-to-end. + +**Step 1 — Create the image pull secret** + +Create a `docker-registry` Secret in the namespace where the chart will be installed (replace ``, ``, and `` with your values): + +```bash +kubectl create secret docker-registry wso2-subscription-creds \ + --namespace \ + --docker-server=registry.wso2.com \ + --docker-username= \ + --docker-password= +``` + +!!! note + Credentials are intentionally kept out of Helm release state. The chart only stores the **name** of the Secret. + +**Step 2 — Set `wso2.subscription.imagePullSecret` in `values.yaml`** + +```yaml +wso2: + subscription: + imagePullSecret: wso2-subscription-creds +``` + +Setting this field causes the chart to: + +- Rewrite all default image repositories from `ghcr.io/wso2/api-platform/` to `registry.wso2.com/wso2-api-platform/` automatically. +- Inject the named Secret into the `imagePullSecrets` block of every component. + +Any explicit `image.repository` override (for example, pointing to an internal mirror) is passed through unchanged, so the rewrite only applies to default images. + +When `wso2.subscription.imagePullSecret` is empty (the default), the chart renders identically to a non-subscription install and pulls from the public GHCR registry with no `imagePullSecrets` block. + +## Setup Steps + +Complete the following steps to configure a production-ready deployment: + +1. [Security Hardening](./production-deployment/security-hardening.md) — encryption keys, TLS, and authentication +2. [Database Configuration](./production-deployment/database-configuration.md) +3. [Resources & Scaling](./production-deployment/resources-and-scaling.md) +4. [Deploy & Verify](./production-deployment/deploy-and-verify.md) +5. [Control Plane Connection](./production-deployment/control-plane-connection.md) *(optional)* diff --git a/en/docs/api-gateway/1.2.0/deployment/production-deployment/control-plane-connection.md b/en/docs/api-gateway/1.2.0/deployment/production-deployment/control-plane-connection.md new file mode 100644 index 000000000..4ddd0a042 --- /dev/null +++ b/en/docs/api-gateway/1.2.0/deployment/production-deployment/control-plane-connection.md @@ -0,0 +1,163 @@ +--- +title: "Control Plane Connection" +description: "Connect API Platform Gateway to a WSO2 APIM or API Platform Cloud control plane for top-down or bottom-up API synchronization." +canonical_url: https://wso2.com/api-platform/docs/api-gateway/deployment/production-deployment/control-plane-connection/ +md_url: https://wso2.com/api-platform/docs/api-gateway/deployment/production-deployment/control-plane-connection.md +tags: + - api-gateway + - deployment + - control-plane +author: WSO2 API Platform Documentation Team +last_updated: 2026-06-11 +content_type: "how-to" +--- + +# Control Plane Connection + +!!! note + This step is optional. Configure it only if the gateway needs to sync API artifacts from the [WSO2 APIM control plane](https://apim.docs.wso2.com/en/latest/api-gateway/platform-gateway/getting-started/) or [API Platform Cloud control plane](https://wso2.com/api-platform/docs/cloud/api-platform-gateway/getting-started/). + +The WSO2 API Platform gateway supports two fundamentally different deployment approaches, distinguished by the direction of API flow. + +=== "Top-Down (Control Plane → Gateway)" + + In this model, the platform control plane pushes APIs to the gateway via a persistent WebSocket connection. Use this approach when APIs are managed centrally and distributed to one or more gateway instances. + + **Create a secret for the registration token:** + + The registration token is generated when you register the gateway with your control plane. Refer to the setup guide for your control plane to obtain it: + + - [WSO2 APIM control plane](https://apim.docs.wso2.com/en/latest/api-gateway/platform-gateway/getting-started/) + - [API Platform Cloud control plane](https://wso2.com/api-platform/docs/cloud/api-platform-gateway/getting-started/) + + ```bash + kubectl create secret generic gateway-cp-token \ + --namespace \ + --from-literal=token='your-registration-token' + ``` + + **Configure the chart:** + + ```yaml + gateway: + config: + controller: + gateway_name: "prod-gateway" + server: + gateway_id: "prod-gateway" + controlplane: + insecure_skip_verify: true + reconnect_initial: 1s + reconnect_max: 5m + polling_interval: 15m + sync_batch_size: 50 + gateway_name: "prod-gateway" # must match the gateway name registered in the controlplane + controller: + controlPlane: + host: "apim-wso2am.example.com:9443" + token: + secretName: "gateway-cp-token" + key: token + ``` + +=== "Bottom-Up (Gateway → On-Prem APIM)" + + In this model, REST APIs deployed directly to the gateway are automatically synced back to an on-premises WSO2 APIM instance. A control plane WebSocket connection is also required alongside the bottom-up sync. API key events, subscription changes, and other lifecycle events are initiated from the control plane devportal and must reach the gateway over this channel. + + !!! note + Bottom-up sync is only supported with APIM 4.7.x. Cloud control plane does not support this flow. + + **Step 1: Create a secret for the registration token** + + The registration token is generated when you register the gateway with your control plane. Refer to the [WSO2 APIM control plane setup guide](https://apim.docs.wso2.com/en/latest/api-gateway/platform-gateway/getting-started/) to obtain it. + + ```bash + kubectl create secret generic gateway-cp-token \ + --namespace \ + --from-literal=token='your-registration-token' + ``` + + **Step 2: Generate OAuth2 client credentials** + + Register a DCR client against your APIM instance to obtain the client ID and secret: + + ```bash + # Use your WSO2 APIM instance's admin credentials here (the APIM control plane's own + # superadmin — not the gateway-controller credential provisioned by setup.sh). + curl -k -X POST https:///client-registration/v0.17/register \ + -H "Content-Type: application/json" \ + -u admin:admin \ + -d '{ + "clientName": "gateway-controller", + "owner": "admin", + "grantType": "client_credentials password refresh_token", + "saasApp": true + }' + ``` + + The response contains `clientId` and `clientSecret`. Use these in the next step. + + **Step 3: Store the OAuth2 credentials in a Kubernetes secret** + + Never place OAuth2 credentials directly in Helm values. Create a secret and inject it via `extraEnvFrom` instead. + + Create `apim-oauth-secret.yaml`: + + ```yaml + apiVersion: v1 + kind: Secret + metadata: + name: apim-oauth-client-secret-secret + namespace: ap-gateway # adjust to match your deployment namespace + type: Opaque + stringData: + APIP_GW_CONTROLLER_CONTROLPLANE_APIM__OAUTH2__CLIENT__ID: "your-client-id" + APIP_GW_CONTROLLER_CONTROLPLANE_APIM__OAUTH2__CLIENT__SECRET: "your-client-secret" + ``` + + Apply it: + + ```bash + kubectl apply -f apim-oauth-secret.yaml + ``` + + **Step 4: Configure the chart** + + Reference both secrets and configure the control plane connection alongside the bottom-up sync settings: + + ```yaml + gateway: + config: + controller: + gateway_name: "prod-gateway" + server: + gateway_id: "prod-gateway" + controlplane: + insecure_skip_verify: true + reconnect_initial: 1s + reconnect_max: 5m + polling_interval: 15m + sync_batch_size: 50 + gateway_name: "prod-gateway" # must match the gateway name registered in the controlplane + # apim_oauth2_client_id and apim_oauth2_client_secret are injected via + # the apim-oauth-client-secret-secret Kubernetes Secret (see extraEnvFrom below). + # Expected env var names: APIP_GW_CONTROLLER__CONTROLPLANE__APIM_OAUTH2__CLIENT_ID + # APIP_GW_CONTROLLER__CONTROLPLANE__APIM_OAUTH2__CLIENT_SECRET + controller: + controlPlane: + host: "apim-wso2am.example.com:9443" + token: + secretName: "gateway-cp-token" # created in Step 1 + key: token + deployment: + extraEnvFrom: + - secretRef: + name: apim-oauth-client-secret-secret + ``` + + !!! tip + If your organization uses the Resource Owner Password Credentials (ROPC) flow instead of client credentials, store `APIP_GW_CONTROLLER__CONTROLPLANE__APIM_OAUTH2__USERNAME` and `APIP_GW_CONTROLLER__CONTROLPLANE__APIM_OAUTH2__PASSWORD` in the secret using the same `extraEnvFrom` pattern. + +--- + +[← Deploy & Verify](./deploy-and-verify.md)  |  [Production Deployment Overview](../high-availability-production-deployment.md) diff --git a/en/docs/api-gateway/1.2.0/deployment/production-deployment/database-configuration.md b/en/docs/api-gateway/1.2.0/deployment/production-deployment/database-configuration.md new file mode 100644 index 000000000..26d041e02 --- /dev/null +++ b/en/docs/api-gateway/1.2.0/deployment/production-deployment/database-configuration.md @@ -0,0 +1,244 @@ +--- +title: "Database Configuration" +description: "Configure PostgreSQL or SQL Server as the shared database for Gateway Controller replicas in high-availability production deployments." +canonical_url: https://wso2.com/api-platform/docs/api-gateway/deployment/production-deployment/database-configuration/ +md_url: https://wso2.com/api-platform/docs/api-gateway/deployment/production-deployment/database-configuration.md +tags: + - api-gateway + - deployment + - postgresql + - sqlserver + - devops +author: WSO2 API Platform Documentation Team +last_updated: 2026-06-26 +content_type: "how-to" +--- + +# Database Configuration + +An external database is required for high-availability production deployments. It removes the single-replica constraint of the default SQLite backend and acts as the shared source of truth across all Gateway Controller replicas. Both **PostgreSQL** and **SQL Server** are supported — use the tabs in each step below to follow the instructions for your database. See [Architecture](../high-availability-production-deployment.md#architecture) for how replicas coordinate via the shared database. + +## Create the Database + +=== "PostgreSQL" + + Connect to your PostgreSQL instance: + + ```bash + psql "host=gateway-postgres.postgres.database.azure.com \ + port=5432 \ + dbname=postgres \ + user= \ + sslmode=require" + ``` + + Create the application database, user, and grant privileges: + + ```sql + CREATE DATABASE gateway_controller; + CREATE USER gateway WITH PASSWORD 'your-db-password'; + GRANT ALL PRIVILEGES ON DATABASE gateway_controller TO gateway; + ``` + +=== "SQL Server" + + Connect to your SQL Server instance (for example, using `sqlcmd`): + + ```bash + sqlcmd -S gateway-sqlserver.database.windows.net,1433 \ + -U -P '' + ``` + + Create the application database, login, and user: + + ```sql + CREATE DATABASE gateway_controller; + GO + CREATE LOGIN gateway WITH PASSWORD = 'your-db-password'; + GO + USE gateway_controller; + GO + CREATE USER gateway FOR LOGIN gateway; + ALTER ROLE db_owner ADD MEMBER gateway; + GO + ``` + +## Apply the Schema + +Apply the schema script for your database before installing the chart. + +=== "PostgreSQL" + + ```bash + psql "host=gateway-postgres.postgres.database.azure.com \ + port=5432 dbname=gateway_controller user= sslmode=require" \ + -v ON_ERROR_STOP=1 -f gateway-controller-db.postgres.sql + ``` + +=== "SQL Server" + + ```bash + sqlcmd -S gateway-sqlserver.database.windows.net,1433 \ + -d gateway_controller -U -P '' -b \ + -i gateway-controller-db.sqlserver.sql + ``` + +The charts do not include a bootstrap job for this, so run it from a CI job, a bastion host, or a temporary pod with network access to the database. If you are also deploying the Event Gateway, apply its supplemental script as well. For the full walkthrough — where to get the scripts, verifying the tables, and restricting runtime privileges — see [Setting Up the Database](../../setup/database-setup.md). + +## Store the Password in a Kubernetes Secret + +The database password is injected as an environment variable from a Kubernetes secret rather than stored in the chart values: + +```bash +kubectl create secret generic gateway-db-password \ + --namespace \ + --from-literal=password='your-db-password' +``` + +## Configure the Chart + +=== "PostgreSQL" + + ```yaml + gateway: + config: + controller: + storage: + type: postgres + postgres: + host: "gateway-postgres.postgres.database.azure.com" + port: 5432 + database: "gateway_controller" + user: "gateway" + sslmode: require + connect_timeout: 5s + max_open_conns: 10 + max_idle_conns: 5 + conn_max_lifetime: 30m + conn_max_idle_time: 5m + application_name: gateway-controller + + controller: + storage: + type: postgres + postgres: + passwordSecretRef: + name: gateway-db-password + key: password + # Disable the SQLite PVC — not needed with an external database + persistence: + enabled: false + ``` + +=== "SQL Server" + + SQL Server uses the unified `database` configuration block. The `options` field controls TLS behavior (`encrypt` and `trust_server_certificate`) in place of PostgreSQL's `sslmode`. + + ```yaml + gateway: + config: + controller: + storage: + type: sqlserver + database: + driver: sqlserver + host: "gateway-sqlserver.database.windows.net" + port: 1433 + database: "gateway_controller" + user: "gateway" + connect_timeout: 5s + max_open_conns: 10 + max_idle_conns: 5 + conn_max_lifetime: 30m + conn_max_idle_time: 5m + application_name: gateway-controller + options: + encrypt: "true" # disable, false, true, strict + trust_server_certificate: "false" + + controller: + storage: + type: sqlserver + sqlserver: + passwordSecretRef: + name: gateway-db-password + key: password + # Disable the SQLite PVC — not needed with an external database + persistence: + enabled: false + ``` + +## DSN Alternative + +If your database connection string is managed externally (for example, from a secrets manager), you can supply a full DSN instead of individual fields. + +=== "PostgreSQL" + + Create a secret containing the DSN: + + ```bash + kubectl create secret generic gateway-db-dsn \ + --namespace \ + --from-literal=dsn='postgres://gateway:your-db-password@postgres.example.internal:5432/gateway_controller?sslmode=require' + ``` + + Reference the secret in values: + + ```yaml + gateway: + config: + controller: + storage: + type: postgres + postgres: + dsn: "postgres://gateway:@postgres.example.internal:5432/gateway_controller?sslmode=require" + controller: + postgres: + passwordSecretRef: + name: gateway-db-dsn + key: dsn + ``` + +=== "SQL Server" + + Create a secret containing the DSN: + + ```bash + kubectl create secret generic gateway-db-dsn \ + --namespace \ + --from-literal=dsn='sqlserver://gateway:your-db-password@sqlserver.example.internal:1433?database=gateway_controller&encrypt=true' + ``` + + Reference the secret in values: + + ```yaml + gateway: + config: + controller: + storage: + type: sqlserver + database: + driver: sqlserver + dsn: "sqlserver://gateway:@sqlserver.example.internal:1433?database=gateway_controller&encrypt=true" + controller: + sqlserver: + passwordSecretRef: + name: gateway-db-dsn + key: dsn + ``` + +!!! note + When `dsn` is set, it takes precedence over all individual connection fields. The password environment variable is still injected from the referenced secret. + +## Connection Pool Tuning + +| Parameter | Default | When to Adjust | +|-----------|---------|----------------| +| `max_open_conns` | `25` | Increase for high-throughput deployments | +| `max_idle_conns` | `5` | Should be ≤ `max_open_conns` | +| `conn_max_lifetime` | `30m` | Reduce if your PostgreSQL has aggressive idle timeouts | +| `conn_max_idle_time` | `5m` | Reduce if connection churn is a concern | + +--- + +[← Security Hardening](./security-hardening.md)  |  [Resources & Scaling →](./resources-and-scaling.md) diff --git a/en/docs/api-gateway/1.2.0/deployment/production-deployment/deploy-and-verify.md b/en/docs/api-gateway/1.2.0/deployment/production-deployment/deploy-and-verify.md new file mode 100644 index 000000000..6e6b5e986 --- /dev/null +++ b/en/docs/api-gateway/1.2.0/deployment/production-deployment/deploy-and-verify.md @@ -0,0 +1,103 @@ +--- +title: "Deploy and Verify the Gateway" +description: "Install API Platform Gateway using Helm, verify that pods and services are healthy, and perform upgrade and rollback procedures." +canonical_url: https://wso2.com/api-platform/docs/api-gateway/deployment/production-deployment/deploy-and-verify/ +md_url: https://wso2.com/api-platform/docs/api-gateway/deployment/production-deployment/deploy-and-verify.md +tags: + - api-gateway + - deployment + - helm + - production +author: WSO2 API Platform Documentation Team +last_updated: 2026-06-17 +content_type: "how-to" +--- + +# Deploy & Verify + +## Deploy the Chart + +=== "OCI Registry (Recommended)" + + ```bash + helm install ap-gateway oci://ghcr.io/wso2/api-platform/helm-charts/gateway \ + --version 1.1.3 \ + --namespace ap-gateway \ + --create-namespace \ + --values ./values.yaml \ + --wait \ + --timeout 5m + ``` + +=== "Local Chart (Testing Only)" + + ```bash + helm install ap-gateway ./kubernetes/helm/gateway-helm-chart \ + --namespace ap-gateway \ + --create-namespace \ + --values ./values.yaml \ + --wait \ + --timeout 5m + ``` + +## Verify the Deployment + +**Check all pods are running:** + +```bash +kubectl get pods -n ap-gateway +``` + +**Check all services:** + +```bash +kubectl get svc -n ap-gateway +``` + +**Controller health check:** + +```bash +kubectl exec -n ap-gateway deploy/ap-gateway-controller -- \ + wget -qO- http://localhost:9094/api/admin/v0.9/health +``` + +## Upgrade Procedure + +**Pull latest chart values to see what changed:** + +```bash +helm show values oci://ghcr.io/wso2/api-platform/helm-charts/gateway --version +``` + +**Diff current release vs the new chart (requires the `helm-diff` plugin):** + +```bash +helm diff upgrade ap-gateway oci://ghcr.io/wso2/api-platform/helm-charts/gateway \ + --version \ + --namespace ap-gateway \ + --values ./values.yaml +``` + +**Upgrade:** + +```bash +helm upgrade ap-gateway oci://ghcr.io/wso2/api-platform/helm-charts/gateway \ + --version \ + --namespace ap-gateway \ + --values ./values.yaml \ + --wait \ + --timeout 5m +``` + +**Rollback if needed:** + +```bash +helm rollback ap-gateway --namespace ap-gateway +``` + +!!! note + The controller pod restarts on upgrade. Because the gateway runtime syncs policy configuration via xDS from the controller, keep `replicaCount` at `≥ 2` for the runtime so in-flight requests continue to be served during controller restarts. + +--- + +[← Resources & Scaling](./resources-and-scaling.md)  |  [Control Plane Connection →](./control-plane-connection.md) *(optional)* diff --git a/en/docs/api-gateway/1.2.0/deployment/production-deployment/resources-and-scaling.md b/en/docs/api-gateway/1.2.0/deployment/production-deployment/resources-and-scaling.md new file mode 100644 index 000000000..48f5d86d6 --- /dev/null +++ b/en/docs/api-gateway/1.2.0/deployment/production-deployment/resources-and-scaling.md @@ -0,0 +1,144 @@ +--- +title: "Configure Resources and Scaling" +description: "Set CPU and memory resource limits, pod anti-affinity rules, Horizontal Pod Autoscaler, and Pod Disruption Budget for API Platform Gateway." +canonical_url: https://wso2.com/api-platform/docs/api-gateway/deployment/production-deployment/resources-and-scaling/ +md_url: https://wso2.com/api-platform/docs/api-gateway/deployment/production-deployment/resources-and-scaling.md +tags: + - api-gateway + - deployment + - kubernetes + - production + - scaling +author: WSO2 API Platform Documentation Team +last_updated: 2026-06-17 +content_type: "how-to" +--- + +# Resources & Scaling + +## Resource Limits + +!!! note + Always set resource limits in production to prevent runaway resource consumption from affecting other workloads on the node. + +**Gateway Controller:** + +```yaml +gateway: + controller: + deployment: + resources: + requests: + cpu: 250m + memory: 256Mi + limits: + cpu: 500m + memory: 512Mi +``` + +**Gateway Runtime:** + +The runtime hosts Envoy and the policy engine. It processes all API traffic. Allocate generously to avoid throttling under load. + +```yaml +gateway: + gatewayRuntime: + deployment: + resources: + requests: + cpu: 500m + memory: 512Mi + limits: + cpu: 2000m + memory: 2Gi +``` + +## Pod Anti-Affinity + +Pod anti-affinity rules control how replicas are spread across your cluster. Two topology keys are most relevant for production: + +| `topologyKey` | Spread scope | When to use | +|---|---|---| +| `kubernetes.io/hostname` | Across nodes | Protects against single-node failure | +| `topology.kubernetes.io/zone` | Across availability zones | Protects against full AZ outage | + +For maximum resilience, combine both. Spread across zones first (higher weight), then across nodes within a zone: + +```yaml +affinity: + podAntiAffinity: + preferredDuringSchedulingIgnoredDuringExecution: + - weight: 100 + podAffinityTerm: + labelSelector: + matchLabels: + app.kubernetes.io/component: controller + topologyKey: topology.kubernetes.io/zone + - weight: 50 + podAffinityTerm: + labelSelector: + matchLabels: + app.kubernetes.io/component: controller + topologyKey: kubernetes.io/hostname +``` + +If your cluster spans only a single availability zone, use `kubernetes.io/hostname` alone: + +```yaml +affinity: + podAntiAffinity: + preferredDuringSchedulingIgnoredDuringExecution: + - weight: 100 + podAffinityTerm: + labelSelector: + matchLabels: + app.kubernetes.io/component: controller + topologyKey: kubernetes.io/hostname +``` + +!!! note + `preferredDuringSchedulingIgnoredDuringExecution` is a soft rule. The scheduler honours it when possible but will still place pods if no suitable node exists. Use `requiredDuringSchedulingIgnoredDuringExecution` if you want a hard guarantee, keeping in mind that pods will remain `Pending` if the constraint cannot be satisfied. + +Apply the same anti-affinity configuration to `gateway.gatewayRuntime` to spread runtime replicas across nodes and zones as well. + +## Horizontal Pod Autoscaler & Pod Disruption Budget + +### Horizontal Pod Autoscaler + +HPA automatically adjusts the number of replicas based on CPU and memory utilization. Apply the following configuration to both `gateway.controller` and `gateway.gatewayRuntime`: + +```yaml +hpa: + enabled: true + minReplicas: 2 + maxReplicas: 5 + targetCPUUtilizationPercentage: 70 + targetMemoryUtilizationPercentage: "" + customMetrics: [] + behavior: {} +``` + +!!! note + Setting `minReplicas: 2` ensures at least two replicas are always running, which is required for high availability. Do not set this to `1` in production. + +### Pod Disruption Budget + +A PDB guarantees that a minimum number of replicas remain available during voluntary disruptions: + +```yaml +podDisruptionBudget: + enabled: true + minAvailable: 50% + maxUnavailable: "" +``` + +The PDB protects against: + +- Node drain operations during cluster upgrades +- Cluster autoscaling scale-down events +- Voluntary pod evictions triggered by resource pressure +- Planned maintenance operations + +--- + +[← Database Configuration](./database-configuration.md)  |  [Deploy & Verify →](./deploy-and-verify.md) diff --git a/en/docs/api-gateway/1.2.0/deployment/production-deployment/security-hardening.md b/en/docs/api-gateway/1.2.0/deployment/production-deployment/security-hardening.md new file mode 100644 index 000000000..050d95206 --- /dev/null +++ b/en/docs/api-gateway/1.2.0/deployment/production-deployment/security-hardening.md @@ -0,0 +1,262 @@ +--- +title: "Security Hardening" +description: "Configure the necessary security areas before deploying API Platform Gateway to production." +canonical_url: https://wso2.com/api-platform/docs/api-gateway/deployment/production-deployment/security-hardening/ +md_url: https://wso2.com/api-platform/docs/api-gateway/deployment/production-deployment/security-hardening.md +tags: + - security + - api-management + - devops +author: WSO2 API Platform Documentation Team +last_updated: 2026-06-11 +content_type: "how-to" +--- + +# Security Hardening + +This page covers the three security areas that must be configured before a production deployment: encryption keys for data at rest, TLS for data in transit, and authentication for access control. + +## Encryption Keys + +The controller uses AES-GCM 256-bit keys to encrypt sensitive data at rest. Providing at least one encryption key is **mandatory**. The chart is fail-closed: it refuses to render unless `gateway.controller.encryptionKeys.enabled` is `true` with a `secretName`, and the controller will not start without its key. + +**Generate a 256-bit AES key:** + +```bash +openssl rand -out default-aesgcm256-v1.bin 32 +``` + +**Create the Kubernetes secret:** + +```bash +kubectl create secret generic gateway-encryption-keys \ + --namespace \ + --from-file=default-aesgcm256-v1.bin=./default-aesgcm256-v1.bin +``` + +**Clean up the local key file:** + +```bash +rm ./default-aesgcm256-v1.bin +``` + +!!! warning + Do not commit the key file to source control. Remove it immediately after creating the Kubernetes secret. + +**Reference the secret in values:** + +```yaml +gateway: + controller: + encryptionKeys: + enabled: true + secretName: gateway-encryption-keys + mountPath: /app/data/aesgcm-keys +``` + +```yaml +gateway: + config: + controller: + encryption: + providers: + - type: aesgcm + keys: + - version: aesgcm256-v1 + file: /app/data/aesgcm-keys/default-aesgcm256-v1.bin +``` + +The `version` field must match the filename stem of the secret key (`default-aesgcm256-v1.bin` → version `aesgcm256-v1`). + +**Key rotation:** + +1. Generate a new key with `openssl rand`. +2. Add it to the Kubernetes secret with an incremented version name. +3. Update the `encryption.providers` list in values to include the new key entry. +4. Run `helm upgrade` to redeploy — the controller picks up the new key on startup. + +!!! note + Keep old key versions in the secret until all data encrypted with the previous key has been re-encrypted or is no longer needed. + +--- + +## TLS Configuration + +TLS must be configured before exposing the gateway externally. Choose one of the options below based on how certificates are managed in your environment. + +=== "Option A: cert-manager" + + cert-manager automates TLS certificate provisioning and renewal within the cluster. Install it if you do not already manage certificates externally. + + **Install cert-manager:** + + ```bash + helm repo add jetstack https://charts.jetstack.io --force-update + helm repo update + + helm install cert-manager jetstack/cert-manager \ + --namespace cert-manager \ + --create-namespace \ + --set crds.enabled=true + ``` + + **Verify all cert-manager pods are running:** + + ```bash + kubectl get pods -n cert-manager + ``` + + **Configure the chart to use cert-manager:** + + ```yaml + gateway: + controller: + tls: + enabled: true + certificateProvider: cert-manager + certManager: + create: true + createIssuer: false # Use your own ClusterIssuer + issuerRef: + name: letsencrypt-prod # Your ClusterIssuer name + kind: ClusterIssuer + commonName: gateway.example.com + dnsNames: + - gateway.example.com + duration: 2160h # 90 days + renewBefore: 720h # Renew 30 days before expiry + ``` + +=== "Option B: Existing TLS Secret" + + Use this option if certificates are managed externally, for example, via a corporate PKI or HashiCorp Vault. + + **Create the TLS secret:** + + ```bash + kubectl create secret tls gateway-tls \ + --namespace \ + --cert=./gateway.crt \ + --key=./gateway.key + ``` + + **Configure the chart to reference the secret:** + + ```yaml + gateway: + controller: + tls: + enabled: true + certificateProvider: secret + secret: + name: gateway-tls + certKey: tls.crt + keyKey: tls.key + ``` + +### Upstream Custom CA Certificates + +If backend services use certificates signed by a private CA, mount the CA bundle into the controller so it can verify upstream TLS connections. + +```bash +kubectl create configmap gateway-upstream-certs \ + --namespace \ + --from-file=private-ca.crt=./my-ca.crt +``` + +```yaml +gateway: + controller: + upstreamCerts: + enabled: true + configMapName: gateway-upstream-certs +``` + +--- + +## Authentication + +!!! warning + The default credentials (`admin`/`admin`) **must** be replaced before deploying to any non-development environment. The controller logs a warning at startup if default or no authentication is configured. + +Choose an authentication strategy based on your organizational requirements. + +=== "Option A: IDP / OAuth2 (Recommended)" + + This is the safest option. No credentials to manage in the cluster. Authentication is delegated entirely to your identity provider. + + ```yaml + gateway: + config: + controller: + auth: + basic: + enabled: false + idp: + enabled: true + jwks_url: "https://idp.example.com/.well-known/jwks.json" + issuer: "https://idp.example.com" + roles_claim: "scope" + role_mapping: + admin: ["gateway:admin"] + developer: ["gateway:developer"] + consumer: ["gateway:consumer"] + ``` + + !!! note + Role mapping values must correspond to claims present in the JWT issued by your IDP. See the [Gateway Controller OpenAPI reference](https://raw.githubusercontent.com/wso2/api-platform/refs/tags/gateway/v1.1.0/gateway/gateway-controller/api/management-openapi.yaml) for the full list of supported roles and their permissions. + +=== "Option B: Basic Auth with Bcrypt" + + If basic auth is required, never store plain-text passwords. The controller supports bcrypt hashes, which are safe to include in Helm values (the hash is not reversible). Store the plain password separately in a Kubernetes secret for rotation reference only. + + **Generate a bcrypt hash:** + + Requires `apache2-utils` (Debian/Ubuntu) or `httpd-tools` (RHEL/CentOS): + + ```bash + htpasswd -nbB admin 'your-secure-password' | cut -d: -f2 + # Output: $2y$10$... + ``` + + On macOS without `htpasswd`: + + ```bash + docker run --rm httpd:alpine htpasswd -nbB admin 'your-secure-password' | cut -d: -f2 + ``` + + **Store the plain password in a Kubernetes secret:** + + Keep the plain password in a secret for rotation reference. It must never appear in Helm values or ConfigMaps: + + ```bash + kubectl create secret generic gateway-admin-credentials \ + --namespace \ + --from-literal=username=admin \ + --from-literal=password='your-secure-password' + ``` + + **Configure the chart with the bcrypt hash:** + + Only the hash goes into the Helm values. This is what ends up in the ConfigMap: + + ```yaml + gateway: + config: + controller: + auth: + basic: + enabled: true + users: + - username: "admin" + password: "$2y$10$..." # bcrypt hash — safe to store in ConfigMap + password_hashed: true + roles: ["admin"] + ``` + + !!! note + Basic auth users are an array of structs and cannot be overridden via environment variables. The hash must be supplied through Helm values. Rotate credentials by generating a new hash, updating the values, and running `helm upgrade`. + +--- + +[← Production Deployment Overview](../high-availability-production-deployment.md)  |  [Database Configuration →](./database-configuration.md) diff --git a/en/docs/api-gateway/1.2.0/gateway-controller-management-api/authentication.md b/en/docs/api-gateway/1.2.0/gateway-controller-management-api/authentication.md new file mode 100644 index 000000000..db566c274 --- /dev/null +++ b/en/docs/api-gateway/1.2.0/gateway-controller-management-api/authentication.md @@ -0,0 +1,104 @@ +--- +title: "Authenticate to the Management API" +description: "Configure Basic Auth or JWT/IDP authentication and role-based authorization for the API Platform Gateway Controller management REST API." +canonical_url: https://wso2.com/api-platform/docs/api-gateway/gateway-controller-management-api/authentication/ +md_url: https://wso2.com/api-platform/docs/api-gateway/gateway-controller-management-api/authentication.md +tags: + - api-gateway + - authentication + - security + - management-api +author: WSO2 API Platform Documentation Team +last_updated: 2026-06-17 +content_type: "how-to" +--- + +# Authentication & Authorization in Gateway Controller + +## Overview +The Gateway Controller REST API (the control-plane API used to manage gateway configuration) can be protected using either locally configured users (Basic Auth) or an external Identity Provider (JWT validation via JWKS). Authorization is role-based and enforced per API route. + +## How It Works + +### Authentication (Who are you?) +You can enable one (or both) of the following: + +- **Basic Auth (local users)**: Define usernames/passwords and assign local roles. +- **IDP/JWT (external users)**: Validate incoming JWTs using `jwks_url` optionally `issuer`. + +**No Authentication (open access)**: If BOTH `basic.enabled` and `idp.enabled` are set to `false`, all requests to the gateway controller are allowed without authentication. + +### Authorization (Are you allowed?) +Gateway Controller routes are protected using **local roles** (for example `admin`, `developer`, `consumer`). + +- If **`roles_claim` is NOT configured** in the IDP/JWT setup, **authorization is bypassed** for the Gateway Controller REST API routes (i.e., no role checks are performed). +- If **`roles_claim` IS configured**, you **must** also configure **`role_mapping`**. Without a mapping, the controller cannot translate IDP roles → local roles, and requests will be denied. + +## Configuration +In the umbrella gateway config, these settings live under `controller.auth`. (If you run the controller standalone, the same structure applies under the controller's config root.) + +### Option A: Basic Auth (local users) +```yaml +controller: + auth: + basic: + enabled: true + users: + - username: "platform-admin" + password: "$bcrypt$..." + password_hashed: true + roles: ["admin"] + - username: "ops" + password: "ops" + password_hashed: false + roles: ["developer"] +``` + +### Option B: JWT (IDP) with role-based authorization +```yaml +controller: + auth: + idp: + enabled: true + jwks_url: "https://idp.example.com/oauth2/jwks" + issuer: "https://idp.example.com/oauth2/token" + + # Turn ON authorization by providing BOTH: + roles_claim: "groups" # e.g., "groups", "scope", "roles" + role_mapping: + admin: ["gateway-admins", "platform-admins"] + developer: ["api-developers", "ops"] + # Optional: give any authenticated user a baseline role + consumer: ["*"] +``` + +## Role Mapping Semantics +`role_mapping` is defined as: + +```text +local_role -> [idp_role_value_1, idp_role_value_2, ...] +``` + +Notes: +- **Specific mappings take precedence** over wildcard matches. +- **Wildcard (`"*"`)** means: if a JWT role value does not match any specific mapping, it can still map to the local role that includes `"*"`. +- **One JWT role can grant multiple local roles** by listing it under multiple local roles. +- **Wildcard mapping must be unique**: Do not configure more than one local role with `"*"` (for example `admin: ["*"]` and `consumer: ["*"]`). The Gateway Controller validates configuration and rejects multiple wildcard roles in `role_mapping`. + +### Example: One IDP group grants multiple local roles +```yaml +role_mapping: + admin: ["platform-admins"] + developer: ["platform-admins", "api-developers"] +``` +In this example, a user in `platform-admins` becomes both `admin` and `developer` in the Gateway Controller. + +## Troubleshooting (What you’ll observe) +- **Requests are denied after enabling JWT auth**: verify `jwks_url` and (if set) `issuer` match the token you're sending. +- **You enabled `roles_claim` and suddenly everything is forbidden**: add `role_mapping` (mapping is mandatory when `roles_claim` is provided). +- **Users authenticate but don't have expected access**: confirm the token actually contains the configured `roles_claim`, and that its values match what you listed in `role_mapping`. +- **You want authN but not authZ**: keep IDP enabled, but leave `roles_claim` and `role_mapping` unset to bypass authorization checks. +- **You want to disable auth entirely**: set both `basic.enabled` and `idp.enabled` to `false`. + +## Testing +Unit tests cover wildcard precedence, one-to-many role grants, and supported claim formats for `roles_claim`. diff --git a/en/docs/api-gateway/1.2.0/gateway-controller-management-api/certificate-management.md b/en/docs/api-gateway/1.2.0/gateway-controller-management-api/certificate-management.md new file mode 100644 index 000000000..832b2c63d --- /dev/null +++ b/en/docs/api-gateway/1.2.0/gateway-controller-management-api/certificate-management.md @@ -0,0 +1,265 @@ +--- +title: "Management API: Certificate Management" +description: "REST API reference for managing custom TLS certificates in API Platform Gateway: list, upload, delete, and reload certificates dynamically." +canonical_url: https://wso2.com/api-platform/docs/api-gateway/gateway-controller-management-api/certificate-management/ +md_url: https://wso2.com/api-platform/docs/api-gateway/gateway-controller-management-api/certificate-management.md +tags: + - api-gateway + - management-api + - certificates + - tls +author: WSO2 API Platform Documentation Team +last_updated: 2026-06-17 +content_type: "reference" +--- + +# Certificate Management + +Manage custom TLS certificates for HTTPS upstream verification + +## List all custom certificates + + + +`GET /certificates` + +> Code samples + +```shell + +curl -X GET http://localhost:9090/api/management/v1/certificates \ + -u {username}:{password} \ + -H 'Accept: application/json' + +``` + +Retrieve all custom TLS certificates currently loaded in the certificate store. +These certificates are used for verifying HTTPS upstream connections. + +### Authentication + + + +> Example responses + +> 200 Response + +```json +{ + "certificates": [ + { + "id": "550e8400-e29b-41d4-a716-446655440000", + "name": "my-custom-ca", + "subject": "CN=My CA,O=My Organization,C=US", + "issuer": "CN=My CA,O=My Organization,C=US", + "notAfter": "2026-11-26 06:07:26", + "count": 1, + "message": "Certificate uploaded and SDS updated successfully", + "status": "success" + } + ], + "totalCount": 3, + "totalBytes": 221599, + "status": "success" +} +``` + +

Responses

+ +|Status|Meaning|Description|Schema| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|List of certificates|[CertificateListResponse](schemas.md#schemacertificatelistresponse)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal server error|[ErrorResponse](schemas.md#schemaerrorresponse)| + +## Upload a new certificate + + + +`POST /certificates` + +> Code samples + +```shell + +curl -X POST http://localhost:9090/api/management/v1/certificates \ + -u {username}:{password} \ + -H 'Content-Type: application/json' \ + -H 'Accept: application/json' \ + -d @payload.json + +``` + +Upload a new TLS certificate (PEM format) to the Gateway. The certificate is loaded dynamically without restarting the Gateway. + +> Payload + +```json +{ + "name": "my-custom-ca", + "certificate": "-----BEGIN CERTIFICATE-----\nMIIDXTCCAkWgAwIBAgIJAKL0UG+mRKtjMA0GCSqGSIb3DQEBCwUAMEUxCzAJBgNV\n...\n-----END CERTIFICATE-----\n" +} +``` + +### Authentication + + + +

Parameters

+ +|Name|In|Type|Required|Description| +|---|---|---|---|---| +|body|body|[CertificateUploadRequest](schemas.md#schemacertificateuploadrequest)|true|none| + +> Example responses + +> 201 Response + +```json +{ + "id": "550e8400-e29b-41d4-a716-446655440000", + "name": "my-custom-ca", + "subject": "CN=My CA,O=My Organization,C=US", + "issuer": "CN=My CA,O=My Organization,C=US", + "notAfter": "2026-11-26 06:07:26", + "count": 1, + "message": "Certificate uploaded and SDS updated successfully", + "status": "success" +} +``` + +

Responses

+ +|Status|Meaning|Description|Schema| +|---|---|---|---| +|201|[Created](https://tools.ietf.org/html/rfc7231#section-6.3.2)|Certificate uploaded successfully|[CertificateResponse](schemas.md#schemacertificateresponse)| +|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Invalid certificate format|[ErrorResponse](schemas.md#schemaerrorresponse)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal server error|[ErrorResponse](schemas.md#schemaerrorresponse)| + +## Delete a certificate + + + +`DELETE /certificates/{id}` + +> Code samples + +```shell + +curl -X DELETE http://localhost:9090/api/management/v1/certificates/{id} \ + -u {username}:{password} \ + -H 'Accept: application/json' + +``` + +Delete a certificate from the Gateway. The change is applied dynamically without restarting the Gateway. + +### Authentication + + + +

Parameters

+ +|Name|In|Type|Required|Description| +|---|---|---|---|---| +|id|path|string|true|ID of the certificate to delete| + +> Example responses + +> 200 Response + +```json +{ + "status": "success", + "message": "Certificate deleted and SDS updated successfully", + "id": "550e8400-e29b-41d4-a716-446655440000" +} +``` + +

Responses

+ +|Status|Meaning|Description|Schema| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|Certificate deleted successfully|Inline| +|404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Certificate not found|[ErrorResponse](schemas.md#schemaerrorresponse)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal server error|[ErrorResponse](schemas.md#schemaerrorresponse)| + +

Response Schema

+ +Status Code **200** + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|status|string|false|none|none| +|message|string|false|none|none| +|id|string|false|none|none| + +## Manually reload certificates + + + +`POST /certificates/reload` + +> Code samples + +```shell + +curl -X POST http://localhost:9090/api/management/v1/certificates/reload \ + -u {username}:{password} \ + -H 'Accept: application/json' + +``` + +Manually trigger a reload of all certificates from the filesystem into the Gateway. + +### Authentication + + + +> Example responses + +> 200 Response + +```json +{ + "status": "success", + "message": "Certificates reloaded and SDS updated successfully", + "totalBytes": 221599 +} +``` + +

Responses

+ +|Status|Meaning|Description|Schema| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|Certificates reloaded successfully|Inline| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal server error|[ErrorResponse](schemas.md#schemaerrorresponse)| + +

Response Schema

+ +Status Code **200** + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|status|string|false|none|none| +|message|string|false|none|none| +|totalBytes|integer|false|none|Total bytes of all loaded certificates| diff --git a/en/docs/api-gateway/1.2.0/gateway-controller-management-api/llm-provider-management.md b/en/docs/api-gateway/1.2.0/gateway-controller-management-api/llm-provider-management.md new file mode 100644 index 000000000..c5739b61b --- /dev/null +++ b/en/docs/api-gateway/1.2.0/gateway-controller-management-api/llm-provider-management.md @@ -0,0 +1,1054 @@ +--- +title: "Management API: LLM Provider Management" +description: "REST API reference for creating, listing, updating, and deleting LLM provider configurations and API keys in API Platform Gateway." +canonical_url: https://wso2.com/api-platform/docs/api-gateway/gateway-controller-management-api/llm-provider-management/ +md_url: https://wso2.com/api-platform/docs/api-gateway/gateway-controller-management-api/llm-provider-management.md +tags: + - api-gateway + - management-api + - llm +author: WSO2 API Platform Documentation Team +last_updated: 2026-06-17 +content_type: "reference" +--- + +# LLM Provider Management + +CRUD operations for LLM Provider configurations + +## Create a new LLM provider + + + +`POST /llm-providers` + +> Code samples + +```shell + +curl -X POST http://localhost:9090/api/management/v1/llm-providers \ + -u {username}:{password} \ + -H 'Content-Type: application/json' \ + -H 'Accept: application/json' \ + -d @payload.json + +``` + +Add a new LLM provider to the Gateway. A provider defines how to interact with an LLM service, including upstream endpoints, authentication, access control, and policies. + +> Payload + +```json +{ + "apiVersion": "gateway.api-platform.wso2.com/v1", + "kind": "LlmProvider", + "metadata": { + "name": "wso2-openai-provider" + }, + "spec": { + "displayName": "OpenAI Provider", + "version": "v1.0", + "template": "openai", + "context": "/openai/latest", + "upstream": { + "url": "https://api.openai.com/v1", + "auth": { + "type": "api-key", + "header": "Authorization", + "value": "Bearer sk-your-api-key" + } + }, + "accessControl": { + "mode": "deny_all", + "exceptions": [ + { + "path": "/chat/completions", + "methods": [ + "POST" + ] + }, + { + "path": "/models", + "methods": [ + "GET" + ] + }, + { + "path": "/models/{modelId}", + "methods": [ + "GET" + ] + } + ] + } + } +} +``` + +### Authentication + + + +

Parameters

+ +|Name|In|Type|Required|Description| +|---|---|---|---|---| +|body|body|[LLMProviderConfigurationRequest](schemas.md#schemallmproviderconfigurationrequest)|true|LLM provider in YAML or JSON format| + +> Example responses + +> 201 Response + +```json +{ + "apiVersion": "gateway.api-platform.wso2.com/v1", + "kind": "LlmProvider", + "metadata": { + "name": "wso2-openai-provider" + }, + "spec": { + "displayName": "OpenAI Provider", + "version": "v1.0", + "template": "openai", + "context": "/openai/latest", + "upstream": { + "url": "https://api.openai.com/v1", + "auth": { + "type": "api-key", + "header": "Authorization", + "value": "Bearer sk-your-api-key" + } + }, + "accessControl": { + "mode": "deny_all", + "exceptions": [ + { + "path": "/chat/completions", + "methods": [ + "POST" + ] + }, + { + "path": "/models", + "methods": [ + "GET" + ] + }, + { + "path": "/models/{modelId}", + "methods": [ + "GET" + ] + } + ] + } + }, + "status": { + "id": "wso2-openai-provider", + "state": "deployed", + "createdAt": "2026-04-24T07:21:13Z", + "updatedAt": "2026-04-24T07:21:13Z", + "deployedAt": "2026-04-24T07:21:13Z" + } +} +``` + +

Responses

+ +|Status|Meaning|Description|Schema| +|---|---|---|---| +|201|[Created](https://tools.ietf.org/html/rfc7231#section-6.3.2)|LLM provider created and deployed successfully|[LLMProviderConfiguration](schemas.md#schemallmproviderconfiguration)| +|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Invalid configuration (validation failed)|[ErrorResponse](schemas.md#schemaerrorresponse)| +|409|[Conflict](https://tools.ietf.org/html/rfc7231#section-6.5.8)|Conflict - Provider with same name and version already exists|[ErrorResponse](schemas.md#schemaerrorresponse)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal server error|[ErrorResponse](schemas.md#schemaerrorresponse)| + +## List all LLM providers + + + +`GET /llm-providers` + +> Code samples + +```shell + +curl -X GET http://localhost:9090/api/management/v1/llm-providers \ + -u {username}:{password} \ + -H 'Accept: application/json' + +``` + +List LLM providers registered in the Gateway, optionally filtered by name, version, context, status, or vhost. + +### Authentication + + + +

Parameters

+ +|Name|In|Type|Required|Description| +|---|---|---|---|---| +|displayName|query|string|false|Filter by LLM provider display name| +|version|query|string|false|Filter by LLM provider version| +|context|query|string|false|Filter by LLM provider context/path| +|status|query|string|false|Filter by deployment status| +|vhost|query|string|false|Filter by LLM provider vhost| + +#### Enumerated Values + +|Parameter|Value| +|---|---| +|status|deployed| +|status|undeployed| + +> Example responses + +> 200 Response + +```json +{ + "status": "success", + "count": 2, + "providers": [ + { + "apiVersion": "gateway.api-platform.wso2.com/v1", + "kind": "LlmProvider", + "metadata": { + "name": "wso2-openai-provider" + }, + "spec": { + "displayName": "OpenAI Provider", + "version": "v1.0", + "template": "openai", + "context": "/openai/latest", + "upstream": { + "url": "https://api.openai.com/v1", + "auth": { + "type": "api-key", + "header": "Authorization", + "value": "Bearer sk-your-api-key" + } + }, + "accessControl": { + "mode": "deny_all", + "exceptions": [ + { + "path": "/chat/completions", + "methods": [ + "POST" + ] + }, + { + "path": "/models", + "methods": [ + "GET" + ] + }, + { + "path": "/models/{modelId}", + "methods": [ + "GET" + ] + } + ] + } + }, + "status": { + "id": "wso2-openai-provider", + "state": "deployed", + "createdAt": "2026-04-24T07:21:13Z", + "updatedAt": "2026-04-24T07:21:13Z", + "deployedAt": "2026-04-24T07:21:13Z" + } + } + ] +} +``` + +

Responses

+ +|Status|Meaning|Description|Schema| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|List of LLM providers|Inline| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal server error|[ErrorResponse](schemas.md#schemaerrorresponse)| + +

Response Schema

+ +Status Code **200** + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|status|string|false|none|none| +|count|integer|false|none|none| +|providers|[allOf]|false|none|none| + +*allOf* + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|*anonymous*|[LLMProviderConfigurationRequest](schemas.md#schemallmproviderconfigurationrequest)|false|none|none| +|apiVersion|string|true|none|Provider specification version| +|kind|string|true|none|Provider kind| +|metadata|[Metadata](schemas.md#schemametadata)|true|none|none| +|name|string|true|none|Unique handle for the resource| +|labels|object|false|none|Labels are key-value pairs for organizing and selecting APIs. Keys must not contain spaces.| +|**additionalProperties**|string|false|none|none| +|annotations|object|false|none|Annotations are arbitrary non-identifying metadata. Use domain-prefixed keys.| +|**additionalProperties**|string|false|none|none| +|spec|[LLMProviderConfigData](schemas.md#schemallmproviderconfigdata)|true|none|none| +|displayName|string|true|none|Human-readable LLM Provider name| +|version|string|true|none|Semantic version of the LLM Provider| +|context|string|false|none|Base path for all API routes (must start with /, no trailing slash)| +|vhost|string|false|none|Virtual host name used for routing. Supports standard domain names, subdomains, or wildcard domains. Must follow RFC-compliant hostname rules. Wildcards are only allowed in the left-most label (e.g., *.example.com).| +|template|string|true|none|Template name to use for this LLM Provider| +|upstream|any|true|none|none| + +*allOf* + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|*anonymous*|[Upstream](schemas.md#schemaupstream)|false|none|Upstream backend configuration (single target or reference)| +|url|string(uri)|false|none|Direct backend URL to route traffic to| +|ref|string|false|none|Reference to a predefined upstreamDefinition| +|hostRewrite|string|false|none|Controls how the Host header is handled when routing to the upstream. `auto` delegates host rewriting to Envoy, which rewrites the Host header using the upstream cluster host. `manual` disables automatic rewriting and expects explicit configuration.| + +*oneOf* + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|*anonymous*|object|false|none|none| + +*xor* + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|*anonymous*|object|false|none|none| + +*and* + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|*anonymous*|[UpstreamAuth](schemas.md#schemaupstreamauth)|false|none|none| +|auth|object|false|none|none| +|type|string|true|none|none| +|header|string|false|none|none| +|value|string|false|none|none| + +*continued* + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|accessControl|[LLMAccessControl](schemas.md#schemallmaccesscontrol)|true|none|none| +|mode|string|true|none|Access control mode| +|exceptions|[[RouteException](schemas.md#schemarouteexception)]|false|none|Path exceptions to the access control mode| +|path|string|true|none|Path pattern| +|methods|[string]|true|none|HTTP methods| +|policies|[[LLMPolicy](schemas.md#schemallmpolicy)]|false|none|List of policies applied only to this operation (overrides or adds to API-level policies)| +|name|string|true|none|none| +|version|string|true|none|none| +|paths|[[LLMPolicyPath](schemas.md#schemallmpolicypath)]|true|none|none| +|path|string|true|none|none| +|methods|[string]|true|none|none| +|params|object|true|none|JSON Schema describing the parameters accepted by this policy. This itself is a JSON Schema document.| +|deploymentState|string|false|none|Desired deployment state - 'deployed' (default) or 'undeployed'. When set to 'undeployed', the LLM Provider is removed from router traffic but configuration and policies are preserved for potential redeployment.| + +*and* + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|*anonymous*|object|false|none|none| +|status|[ResourceStatus](schemas.md#schemaresourcestatus)|false|read-only|Server-managed lifecycle fields. Populated on responses.| +|id|string|false|none|Unique identifier assigned by the server (equal to metadata.name)| +|state|string|false|none|Desired deployment state reported by the server| +|createdAt|string(date-time)|false|none|Timestamp when the resource was first created (UTC)| +|updatedAt|string(date-time)|false|none|Timestamp when the resource was last updated (UTC)| +|deployedAt|string(date-time)|false|none|Timestamp when the resource was last deployed (omitted when undeployed)| + +#### Enumerated Values + +|Property|Value| +|---|---| +|apiVersion|gateway.api-platform.wso2.com/v1| +|kind|LlmProvider| +|hostRewrite|auto| +|hostRewrite|manual| +|type|api-key| +|mode|allow_all| +|mode|deny_all| +|deploymentState|deployed| +|deploymentState|undeployed| +|state|deployed| +|state|undeployed| + +## Get LLM provider by identifier + + + +`GET /llm-providers/{id}` + +> Code samples + +```shell + +curl -X GET http://localhost:9090/api/management/v1/llm-providers/{id} \ + -u {username}:{password} \ + -H 'Accept: application/json' + +``` + +Get an LLM provider by its ID. + +### Authentication + + + +

Parameters

+ +|Name|In|Type|Required|Description| +|---|---|---|---|---| +|id|path|string|true|Unique identifier of the LLM provider| + +> Example responses + +> 200 Response + +```json +{ + "apiVersion": "gateway.api-platform.wso2.com/v1", + "kind": "LlmProvider", + "metadata": { + "name": "wso2-openai-provider" + }, + "spec": { + "displayName": "OpenAI Provider", + "version": "v1.0", + "template": "openai", + "context": "/openai/latest", + "upstream": { + "url": "https://api.openai.com/v1", + "auth": { + "type": "api-key", + "header": "Authorization", + "value": "Bearer sk-your-api-key" + } + }, + "accessControl": { + "mode": "deny_all", + "exceptions": [ + { + "path": "/chat/completions", + "methods": [ + "POST" + ] + }, + { + "path": "/models", + "methods": [ + "GET" + ] + }, + { + "path": "/models/{modelId}", + "methods": [ + "GET" + ] + } + ] + } + }, + "status": { + "id": "wso2-openai-provider", + "state": "deployed", + "createdAt": "2026-04-24T07:21:13Z", + "updatedAt": "2026-04-24T07:21:13Z", + "deployedAt": "2026-04-24T07:21:13Z" + } +} +``` + +

Responses

+ +|Status|Meaning|Description|Schema| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|LLM provider details|[LLMProviderConfiguration](schemas.md#schemallmproviderconfiguration)| +|404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|LLM provider not found|[ErrorResponse](schemas.md#schemaerrorresponse)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal server error|[ErrorResponse](schemas.md#schemaerrorresponse)| + +## Update an existing LLM provider + + + +`PUT /llm-providers/{id}` + +> Code samples + +```shell + +curl -X PUT http://localhost:9090/api/management/v1/llm-providers/{id} \ + -u {username}:{password} \ + -H 'Content-Type: application/json' \ + -H 'Accept: application/json' \ + -d @payload.json + +``` + +Update an existing LLM provider in the Gateway. + +> Payload + +```json +{ + "apiVersion": "gateway.api-platform.wso2.com/v1", + "kind": "LlmProvider", + "metadata": { + "name": "wso2-openai-provider" + }, + "spec": { + "displayName": "OpenAI Provider", + "version": "v1.0", + "template": "openai", + "context": "/openai/latest", + "upstream": { + "url": "https://api.openai.com/v1", + "auth": { + "type": "api-key", + "header": "Authorization", + "value": "Bearer sk-your-api-key" + } + }, + "accessControl": { + "mode": "deny_all", + "exceptions": [ + { + "path": "/chat/completions", + "methods": [ + "POST" + ] + }, + { + "path": "/models", + "methods": [ + "GET" + ] + }, + { + "path": "/models/{modelId}", + "methods": [ + "GET" + ] + } + ] + } + } +} +``` + +### Authentication + + + +

Parameters

+ +|Name|In|Type|Required|Description| +|---|---|---|---|---| +|id|path|string|true|Unique identifier of the LLM provider| +|body|body|[LLMProviderConfigurationRequest](schemas.md#schemallmproviderconfigurationrequest)|true|Updated LLM provider| + +> Example responses + +> 200 Response + +```json +{ + "apiVersion": "gateway.api-platform.wso2.com/v1", + "kind": "LlmProvider", + "metadata": { + "name": "wso2-openai-provider" + }, + "spec": { + "displayName": "OpenAI Provider", + "version": "v1.0", + "template": "openai", + "context": "/openai/latest", + "upstream": { + "url": "https://api.openai.com/v1", + "auth": { + "type": "api-key", + "header": "Authorization", + "value": "Bearer sk-your-api-key" + } + }, + "accessControl": { + "mode": "deny_all", + "exceptions": [ + { + "path": "/chat/completions", + "methods": [ + "POST" + ] + }, + { + "path": "/models", + "methods": [ + "GET" + ] + }, + { + "path": "/models/{modelId}", + "methods": [ + "GET" + ] + } + ] + } + }, + "status": { + "id": "wso2-openai-provider", + "state": "deployed", + "createdAt": "2026-04-24T07:21:13Z", + "updatedAt": "2026-04-24T07:21:13Z", + "deployedAt": "2026-04-24T07:21:13Z" + } +} +``` + +

Responses

+ +|Status|Meaning|Description|Schema| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|LLM provider updated successfully|[LLMProviderConfiguration](schemas.md#schemallmproviderconfiguration)| +|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Invalid configuration|[ErrorResponse](schemas.md#schemaerrorresponse)| +|404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|LLM provider not found|[ErrorResponse](schemas.md#schemaerrorresponse)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal server error|[ErrorResponse](schemas.md#schemaerrorresponse)| + +## Delete an LLM provider + + + +`DELETE /llm-providers/{id}` + +> Code samples + +```shell + +curl -X DELETE http://localhost:9090/api/management/v1/llm-providers/{id} \ + -u {username}:{password} \ + -H 'Accept: application/json' + +``` + +Delete an LLM provider from the Gateway. + +### Authentication + + + +

Parameters

+ +|Name|In|Type|Required|Description| +|---|---|---|---|---| +|id|path|string|true|Unique identifier of the LLM provider| + +> Example responses + +> 200 Response + +```json +{ + "status": "success", + "message": "LLM provider deleted successfully", + "id": "wso2-openai-provider" +} +``` + +

Responses

+ +|Status|Meaning|Description|Schema| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|LLM provider deleted successfully|Inline| +|404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|LLM provider not found|[ErrorResponse](schemas.md#schemaerrorresponse)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal server error|[ErrorResponse](schemas.md#schemaerrorresponse)| + +

Response Schema

+ +Status Code **200** + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|status|string|false|none|none| +|message|string|false|none|none| +|id|string|false|none|none| + +## Create a new API key for an LLM provider + + + +`POST /llm-providers/{id}/api-keys` + +> Code samples + +```shell + +curl -X POST http://localhost:9090/api/management/v1/llm-providers/{id}/api-keys \ + -u {username}:{password} \ + -H 'Content-Type: application/json' \ + -H 'Accept: application/json' \ + -d @payload.json + +``` + +Generate a new API key for an LLM provider in the Gateway. + +> Payload + +```json +{ + "name": "my-production-key" +} +``` + +### Authentication + + + +

Parameters

+ +|Name|In|Type|Required|Description| +|---|---|---|---|---| +|id|path|string|true|Unique handle of the LLM provider to generate the key for| +|body|body|[APIKeyCreationRequest](schemas.md#schemaapikeycreationrequest)|true|none| + +> Example responses + +> 201 Response + +```json +{ + "status": "success", + "message": "API key generated successfully", + "remainingApiKeyQuota": 9, + "apiKey": { + "name": "my-production-key", + "displayName": "My Production Key", + "apiKey": "apip_1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef", + "apiId": "reading-list-api-v1.0", + "status": "active", + "createdAt": "2026-04-01T10:30:00Z", + "createdBy": "admin", + "expiresAt": null, + "source": "local" + } +} +``` + +

Responses

+ +|Status|Meaning|Description|Schema| +|---|---|---|---| +|201|[Created](https://tools.ietf.org/html/rfc7231#section-6.3.2)|API key created successfully|[APIKeyCreationResponse](schemas.md#schemaapikeycreationresponse)| +|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Invalid configuration (validation failed)|[ErrorResponse](schemas.md#schemaerrorresponse)| +|404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|LLM provider not found|[ErrorResponse](schemas.md#schemaerrorresponse)| +|409|[Conflict](https://tools.ietf.org/html/rfc7231#section-6.5.8)|Conflict (duplicate key or conflicting update)|[ErrorResponse](schemas.md#schemaerrorresponse)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal server error|[ErrorResponse](schemas.md#schemaerrorresponse)| + +## Get the list of API keys for an LLM provider + + + +`GET /llm-providers/{id}/api-keys` + +> Code samples + +```shell + +curl -X GET http://localhost:9090/api/management/v1/llm-providers/{id}/api-keys \ + -u {username}:{password} \ + -H 'Accept: application/json' + +``` + +List all API keys for an LLM provider in the Gateway. + +### Authentication + + + +

Parameters

+ +|Name|In|Type|Required|Description| +|---|---|---|---|---| +|id|path|string|true|Unique handle of the LLM provider to retrieve keys for| + +> Example responses + +> 200 Response + +```json +{ + "apiKeys": [ + { + "name": "my-production-key", + "displayName": "My Production Key", + "apiKey": "apip_1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef", + "apiId": "reading-list-api-v1.0", + "status": "active", + "createdAt": "2026-04-01T10:30:00Z", + "createdBy": "admin", + "expiresAt": null, + "source": "local" + } + ], + "totalCount": 3, + "status": "success" +} +``` + +

Responses

+ +|Status|Meaning|Description|Schema| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|List of API keys|[APIKeyListResponse](schemas.md#schemaapikeylistresponse)| +|404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|LLM provider not found|[ErrorResponse](schemas.md#schemaerrorresponse)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal server error|[ErrorResponse](schemas.md#schemaerrorresponse)| + +## Regenerate API key for an LLM provider + + + +`POST /llm-providers/{id}/api-keys/{apiKeyName}/regenerate` + +> Code samples + +```shell + +curl -X POST http://localhost:9090/api/management/v1/llm-providers/{id}/api-keys/{apiKeyName}/regenerate \ + -u {username}:{password} \ + -H 'Content-Type: application/json' \ + -H 'Accept: application/json' \ + -d @payload.json + +``` + +Regenerate an existing API key for an LLM provider in the Gateway. + +> Payload + +```json +{} +``` + +### Authentication + + + +

Parameters

+ +|Name|In|Type|Required|Description| +|---|---|---|---|---| +|id|path|string|true|Unique handle of the LLM provider| +|apiKeyName|path|string|true|Name of the API key to regenerate| +|body|body|[APIKeyRegenerationRequest](schemas.md#schemaapikeyregenerationrequest)|true|none| + +> Example responses + +> 200 Response + +```json +{ + "status": "success", + "message": "API key generated successfully", + "remainingApiKeyQuota": 9, + "apiKey": { + "name": "my-production-key", + "displayName": "My Production Key", + "apiKey": "apip_1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef", + "apiId": "reading-list-api-v1.0", + "status": "active", + "createdAt": "2026-04-01T10:30:00Z", + "createdBy": "admin", + "expiresAt": null, + "source": "local" + } +} +``` + +

Responses

+ +|Status|Meaning|Description|Schema| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|API key rotated successfully|[APIKeyCreationResponse](schemas.md#schemaapikeycreationresponse)| +|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Invalid configuration (validation failed)|[ErrorResponse](schemas.md#schemaerrorresponse)| +|404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|LLM provider or API key not found|[ErrorResponse](schemas.md#schemaerrorresponse)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal server error|[ErrorResponse](schemas.md#schemaerrorresponse)| + +## Update an API key for an LLM provider + + + +`PUT /llm-providers/{id}/api-keys/{apiKeyName}` + +> Code samples + +```shell + +curl -X PUT http://localhost:9090/api/management/v1/llm-providers/{id}/api-keys/{apiKeyName} \ + -u {username}:{password} \ + -H 'Content-Type: application/json' \ + -H 'Accept: application/json' \ + -d @payload.json + +``` + +Update an API key with a custom value instead of auto-generating one. + +> Payload + +```json +{ + "name": "my-production-key" +} +``` + +### Authentication + + + +

Parameters

+ +|Name|In|Type|Required|Description| +|---|---|---|---|---| +|id|path|string|true|Unique handle of the LLM provider| +|apiKeyName|path|string|true|Name of the API key to update| +|body|body|[APIKeyUpdateRequest](schemas.md#schemaapikeyupdaterequest)|true|none| + +> Example responses + +> 200 Response + +```json +{ + "status": "success", + "message": "API key generated successfully", + "remainingApiKeyQuota": 9, + "apiKey": { + "name": "my-production-key", + "displayName": "My Production Key", + "apiKey": "apip_1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef", + "apiId": "reading-list-api-v1.0", + "status": "active", + "createdAt": "2026-04-01T10:30:00Z", + "createdBy": "admin", + "expiresAt": null, + "source": "local" + } +} +``` + +

Responses

+ +|Status|Meaning|Description|Schema| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|API key updated successfully|[APIKeyCreationResponse](schemas.md#schemaapikeycreationresponse)| +|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Invalid request (validation failed)|[ErrorResponse](schemas.md#schemaerrorresponse)| +|404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|LLM provider or API key not found|[ErrorResponse](schemas.md#schemaerrorresponse)| +|409|[Conflict](https://tools.ietf.org/html/rfc7231#section-6.5.8)|Conflict (duplicate key or conflicting update)|[ErrorResponse](schemas.md#schemaerrorresponse)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal server error|[ErrorResponse](schemas.md#schemaerrorresponse)| + +## Revoke an API key for an LLM provider + + + +`DELETE /llm-providers/{id}/api-keys/{apiKeyName}` + +> Code samples + +```shell + +curl -X DELETE http://localhost:9090/api/management/v1/llm-providers/{id}/api-keys/{apiKeyName} \ + -u {username}:{password} \ + -H 'Accept: application/json' + +``` + +Revoke an API key. Once revoked, it can no longer be used to authenticate requests. + +### Authentication + + + +

Parameters

+ +|Name|In|Type|Required|Description| +|---|---|---|---|---| +|id|path|string|true|Unique handle of the LLM provider| +|apiKeyName|path|string|true|Name of the API key to revoke| + +> Example responses + +> 200 Response + +```json +{ + "status": "success", + "message": "API key revoked successfully" +} +``` + +

Responses

+ +|Status|Meaning|Description|Schema| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|API key revoked successfully|[APIKeyRevocationResponse](schemas.md#schemaapikeyrevocationresponse)| +|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Invalid configuration (validation failed)|[ErrorResponse](schemas.md#schemaerrorresponse)| +|404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|LLM provider or API key not found|[ErrorResponse](schemas.md#schemaerrorresponse)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal server error|[ErrorResponse](schemas.md#schemaerrorresponse)| diff --git a/en/docs/api-gateway/1.2.0/gateway-controller-management-api/llm-provider-template-management.md b/en/docs/api-gateway/1.2.0/gateway-controller-management-api/llm-provider-template-management.md new file mode 100644 index 000000000..0042a2793 --- /dev/null +++ b/en/docs/api-gateway/1.2.0/gateway-controller-management-api/llm-provider-template-management.md @@ -0,0 +1,600 @@ +--- +title: "Management API: LLM Provider Template Management" +description: "REST API reference for creating, listing, updating, and deleting LLM provider templates in API Platform Gateway." +canonical_url: https://wso2.com/api-platform/docs/api-gateway/gateway-controller-management-api/llm-provider-template-management/ +md_url: https://wso2.com/api-platform/docs/api-gateway/gateway-controller-management-api/llm-provider-template-management.md +tags: + - api-gateway + - management-api + - llm +author: WSO2 API Platform Documentation Team +last_updated: 2026-06-17 +content_type: "reference" +--- + +# LLM Provider Template Management + +CRUD operations for LLM Provider Template configurations + +## Create a new LLM provider template + + + +`POST /llm-provider-templates` + +> Code samples + +```shell + +curl -X POST http://localhost:9090/api/management/v1/llm-provider-templates \ + -u {username}:{password} \ + -H 'Content-Type: application/json' \ + -H 'Accept: application/json' \ + -d @payload.json + +``` + +Add a new LLM provider template to the Gateway. A template defines token tracking and model extraction metadata for an LLM provider. + +> Payload + +```json +{ + "apiVersion": "gateway.api-platform.wso2.com/v1", + "kind": "LlmProviderTemplate", + "metadata": { + "name": "openai-template" + }, + "spec": { + "displayName": "OpenAI", + "promptTokens": { + "location": "payload", + "identifier": "$.usage.prompt_tokens" + }, + "completionTokens": { + "location": "payload", + "identifier": "$.usage.completion_tokens" + }, + "totalTokens": { + "location": "payload", + "identifier": "$.usage.total_tokens" + }, + "remainingTokens": { + "location": "header", + "identifier": "x-ratelimit-remaining-tokens" + }, + "requestModel": { + "location": "payload", + "identifier": "$.model" + }, + "responseModel": { + "location": "payload", + "identifier": "$.model" + } + } +} +``` + +!!! note "Using a YAML downloaded from AI Workspace" + If you downloaded the template YAML from AI Workspace, you have two options to use it with this endpoint: + + - **Convert to JSON:** Convert the YAML file to JSON before passing it as the request body with `-H 'Content-Type: application/json'`. + - **Send as YAML:** Pass the YAML file directly and change the content type header to `application/yaml`: + + ```shell + curl -X POST http://localhost:9090/api/management/v1/llm-provider-templates \ + -u {username}:{password} \ + -H 'Content-Type: application/yaml' \ + -H 'Accept: application/json' \ + --data-binary @template.yaml + ``` + +### Authentication + + + +

Parameters

+ +|Name|In|Type|Required|Description| +|---|---|---|---|---| +|body|body|[LLMProviderTemplateRequest](schemas.md#schemallmprovidertemplaterequest)|true|none| + +> Example responses + +> 201 Response + +```json +{ + "apiVersion": "gateway.api-platform.wso2.com/v1", + "kind": "LlmProviderTemplate", + "metadata": { + "name": "openai-template" + }, + "spec": { + "displayName": "OpenAI", + "promptTokens": { + "location": "payload", + "identifier": "$.usage.prompt_tokens" + }, + "completionTokens": { + "location": "payload", + "identifier": "$.usage.completion_tokens" + }, + "totalTokens": { + "location": "payload", + "identifier": "$.usage.total_tokens" + }, + "remainingTokens": { + "location": "header", + "identifier": "x-ratelimit-remaining-tokens" + }, + "requestModel": { + "location": "payload", + "identifier": "$.model" + }, + "responseModel": { + "location": "payload", + "identifier": "$.model" + } + }, + "status": { + "id": "openai-template", + "createdAt": "2026-04-24T07:21:13Z", + "updatedAt": "2026-04-24T07:21:13Z" + } +} +``` + +

Responses

+ +|Status|Meaning|Description|Schema| +|---|---|---|---| +|201|[Created](https://tools.ietf.org/html/rfc7231#section-6.3.2)|LLM provider template created successfully|[LLMProviderTemplate](schemas.md#schemallmprovidertemplate)| +|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Invalid configuration (validation failed)|[ErrorResponse](schemas.md#schemaerrorresponse)| +|409|[Conflict](https://tools.ietf.org/html/rfc7231#section-6.5.8)|Conflict - Template with same name already exists|[ErrorResponse](schemas.md#schemaerrorresponse)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal server error|[ErrorResponse](schemas.md#schemaerrorresponse)| + +## List all LLM provider templates + + + +`GET /llm-provider-templates` + +> Code samples + +```shell + +curl -X GET http://localhost:9090/api/management/v1/llm-provider-templates \ + -u {username}:{password} \ + -H 'Accept: application/json' + +``` + +List LLM provider templates registered in the Gateway, optionally filtered by name. + +### Authentication + + + +

Parameters

+ +|Name|In|Type|Required|Description| +|---|---|---|---|---| +|displayName|query|string|false|Filter by template display name| + +> Example responses + +> 200 Response + +```json +{ + "status": "success", + "count": 3, + "templates": [ + { + "apiVersion": "gateway.api-platform.wso2.com/v1", + "kind": "LlmProviderTemplate", + "metadata": { + "name": "openai-template" + }, + "spec": { + "displayName": "OpenAI", + "promptTokens": { + "location": "payload", + "identifier": "$.usage.prompt_tokens" + }, + "completionTokens": { + "location": "payload", + "identifier": "$.usage.completion_tokens" + }, + "totalTokens": { + "location": "payload", + "identifier": "$.usage.total_tokens" + }, + "remainingTokens": { + "location": "header", + "identifier": "x-ratelimit-remaining-tokens" + }, + "requestModel": { + "location": "payload", + "identifier": "$.model" + }, + "responseModel": { + "location": "payload", + "identifier": "$.model" + } + }, + "status": { + "id": "openai-template", + "createdAt": "2026-04-24T07:21:13Z", + "updatedAt": "2026-04-24T07:21:13Z" + } + } + ] +} +``` + +

Responses

+ +|Status|Meaning|Description|Schema| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|List of LLM provider templates|Inline| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal server error|[ErrorResponse](schemas.md#schemaerrorresponse)| + +

Response Schema

+ +Status Code **200** + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|status|string|false|none|none| +|count|integer|false|none|none| +|templates|[allOf]|false|none|none| + +*allOf* + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|*anonymous*|[LLMProviderTemplateRequest](schemas.md#schemallmprovidertemplaterequest)|false|none|none| +|apiVersion|string|true|none|Template specification version| +|kind|string|true|none|Template kind| +|metadata|[Metadata](schemas.md#schemametadata)|true|none|none| +|name|string|true|none|Unique handle for the resource| +|labels|object|false|none|Labels are key-value pairs for organizing and selecting APIs. Keys must not contain spaces.| +|**additionalProperties**|string|false|none|none| +|annotations|object|false|none|Annotations are arbitrary non-identifying metadata. Use domain-prefixed keys.| +|**additionalProperties**|string|false|none|none| +|spec|[LLMProviderTemplateData](schemas.md#schemallmprovidertemplatedata)|true|none|none| +|displayName|string|true|none|Human-readable LLM Template name| +|promptTokens|[ExtractionIdentifier](schemas.md#schemaextractionidentifier)|false|none|none| +|location|string|true|none|Where to find the token information| +|identifier|string|true|none|JSONPath expression or header name to identify the token value| +|completionTokens|[ExtractionIdentifier](schemas.md#schemaextractionidentifier)|false|none|none| +|totalTokens|[ExtractionIdentifier](schemas.md#schemaextractionidentifier)|false|none|none| +|remainingTokens|[ExtractionIdentifier](schemas.md#schemaextractionidentifier)|false|none|none| +|requestModel|[ExtractionIdentifier](schemas.md#schemaextractionidentifier)|false|none|none| +|responseModel|[ExtractionIdentifier](schemas.md#schemaextractionidentifier)|false|none|none| +|resourceMappings|[LLMProviderTemplateResourceMappings](schemas.md#schemallmprovidertemplateresourcemappings)|false|none|none| +|resources|[[LLMProviderTemplateResourceMapping](schemas.md#schemallmprovidertemplateresourcemapping)]|false|none|none| +|resource|string|true|none|Resource path pattern for this mapping| +|promptTokens|[ExtractionIdentifier](schemas.md#schemaextractionidentifier)|false|none|none| +|completionTokens|[ExtractionIdentifier](schemas.md#schemaextractionidentifier)|false|none|none| +|totalTokens|[ExtractionIdentifier](schemas.md#schemaextractionidentifier)|false|none|none| +|remainingTokens|[ExtractionIdentifier](schemas.md#schemaextractionidentifier)|false|none|none| +|requestModel|[ExtractionIdentifier](schemas.md#schemaextractionidentifier)|false|none|none| +|responseModel|[ExtractionIdentifier](schemas.md#schemaextractionidentifier)|false|none|none| + +*and* + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|*anonymous*|object|false|none|none| +|status|[ResourceStatus](schemas.md#schemaresourcestatus)|false|read-only|Server-managed lifecycle fields. Populated on responses.| +|id|string|false|none|Unique identifier assigned by the server (equal to metadata.name)| +|state|string|false|none|Desired deployment state reported by the server| +|createdAt|string(date-time)|false|none|Timestamp when the resource was first created (UTC)| +|updatedAt|string(date-time)|false|none|Timestamp when the resource was last updated (UTC)| +|deployedAt|string(date-time)|false|none|Timestamp when the resource was last deployed (omitted when undeployed)| + +#### Enumerated Values + +|Property|Value| +|---|---| +|apiVersion|gateway.api-platform.wso2.com/v1| +|kind|LlmProviderTemplate| +|location|payload| +|location|header| +|location|queryParam| +|location|pathParam| +|state|deployed| +|state|undeployed| + +## Get LLM provider template by id + + + +`GET /llm-provider-templates/{id}` + +> Code samples + +```shell + +curl -X GET http://localhost:9090/api/management/v1/llm-provider-templates/{id} \ + -u {username}:{password} \ + -H 'Accept: application/json' + +``` + +Get an LLM provider template by its ID. + +### Authentication + + + +

Parameters

+ +|Name|In|Type|Required|Description| +|---|---|---|---|---| +|id|path|string|true|Unique public identifier for the LLM provider template| + +> Example responses + +> 200 Response + +```json +{ + "apiVersion": "gateway.api-platform.wso2.com/v1", + "kind": "LlmProviderTemplate", + "metadata": { + "name": "openai-template" + }, + "spec": { + "displayName": "OpenAI", + "promptTokens": { + "location": "payload", + "identifier": "$.usage.prompt_tokens" + }, + "completionTokens": { + "location": "payload", + "identifier": "$.usage.completion_tokens" + }, + "totalTokens": { + "location": "payload", + "identifier": "$.usage.total_tokens" + }, + "remainingTokens": { + "location": "header", + "identifier": "x-ratelimit-remaining-tokens" + }, + "requestModel": { + "location": "payload", + "identifier": "$.model" + }, + "responseModel": { + "location": "payload", + "identifier": "$.model" + } + }, + "status": { + "id": "openai-template", + "createdAt": "2026-04-24T07:21:13Z", + "updatedAt": "2026-04-24T07:21:13Z" + } +} +``` + +

Responses

+ +|Status|Meaning|Description|Schema| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|LLM provider template details|[LLMProviderTemplate](schemas.md#schemallmprovidertemplate)| +|404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|LLM provider template not found|[ErrorResponse](schemas.md#schemaerrorresponse)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal server error|[ErrorResponse](schemas.md#schemaerrorresponse)| + +## Update an existing LLM provider template + + + +`PUT /llm-provider-templates/{id}` + +> Code samples + +```shell + +curl -X PUT http://localhost:9090/api/management/v1/llm-provider-templates/{id} \ + -u {username}:{password} \ + -H 'Content-Type: application/json' \ + -H 'Accept: application/json' \ + -d @payload.json + +``` + +Update an existing LLM provider template in the Gateway. + +> Payload + +```json +{ + "apiVersion": "gateway.api-platform.wso2.com/v1", + "kind": "LlmProviderTemplate", + "metadata": { + "name": "openai-template" + }, + "spec": { + "displayName": "OpenAI", + "promptTokens": { + "location": "payload", + "identifier": "$.usage.prompt_tokens" + }, + "completionTokens": { + "location": "payload", + "identifier": "$.usage.completion_tokens" + }, + "totalTokens": { + "location": "payload", + "identifier": "$.usage.total_tokens" + }, + "remainingTokens": { + "location": "header", + "identifier": "x-ratelimit-remaining-tokens" + }, + "requestModel": { + "location": "payload", + "identifier": "$.model" + }, + "responseModel": { + "location": "payload", + "identifier": "$.model" + } + } +} +``` + +### Authentication + + + +

Parameters

+ +|Name|In|Type|Required|Description| +|---|---|---|---|---| +|id|path|string|true|Unique public identifier of the template to update| +|body|body|[LLMProviderTemplateRequest](schemas.md#schemallmprovidertemplaterequest)|true|none| + +> Example responses + +> 200 Response + +```json +{ + "apiVersion": "gateway.api-platform.wso2.com/v1", + "kind": "LlmProviderTemplate", + "metadata": { + "name": "openai-template" + }, + "spec": { + "displayName": "OpenAI", + "promptTokens": { + "location": "payload", + "identifier": "$.usage.prompt_tokens" + }, + "completionTokens": { + "location": "payload", + "identifier": "$.usage.completion_tokens" + }, + "totalTokens": { + "location": "payload", + "identifier": "$.usage.total_tokens" + }, + "remainingTokens": { + "location": "header", + "identifier": "x-ratelimit-remaining-tokens" + }, + "requestModel": { + "location": "payload", + "identifier": "$.model" + }, + "responseModel": { + "location": "payload", + "identifier": "$.model" + } + }, + "status": { + "id": "openai-template", + "createdAt": "2026-04-24T07:21:13Z", + "updatedAt": "2026-04-24T07:21:13Z" + } +} +``` + +

Responses

+ +|Status|Meaning|Description|Schema| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|LLM provider template updated successfully|[LLMProviderTemplate](schemas.md#schemallmprovidertemplate)| +|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Invalid configuration (validation failed)|[ErrorResponse](schemas.md#schemaerrorresponse)| +|404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|LLM provider template not found|[ErrorResponse](schemas.md#schemaerrorresponse)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal server error|[ErrorResponse](schemas.md#schemaerrorresponse)| + +## Delete an LLM provider template + + + +`DELETE /llm-provider-templates/{id}` + +> Code samples + +```shell + +curl -X DELETE http://localhost:9090/api/management/v1/llm-provider-templates/{id} \ + -u {username}:{password} \ + -H 'Accept: application/json' + +``` + +Delete an LLM provider template from the Gateway. + +### Authentication + + + +

Parameters

+ +|Name|In|Type|Required|Description| +|---|---|---|---|---| +|id|path|string|true|Unique public identifier of the template to delete| + +> Example responses + +> 200 Response + +```json +{ + "status": "success", + "message": "LLM provider template deleted successfully", + "id": "openai" +} +``` + +

Responses

+ +|Status|Meaning|Description|Schema| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|LLM provider template deleted successfully|Inline| +|404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|LLM provider template not found|[ErrorResponse](schemas.md#schemaerrorresponse)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal server error|[ErrorResponse](schemas.md#schemaerrorresponse)| + +

Response Schema

+ +Status Code **200** + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|status|string|false|none|none| +|message|string|false|none|none| +|id|string|false|none|none| diff --git a/en/docs/api-gateway/1.2.0/gateway-controller-management-api/llm-proxy-management.md b/en/docs/api-gateway/1.2.0/gateway-controller-management-api/llm-proxy-management.md new file mode 100644 index 000000000..9a980a985 --- /dev/null +++ b/en/docs/api-gateway/1.2.0/gateway-controller-management-api/llm-proxy-management.md @@ -0,0 +1,845 @@ +--- +title: "Management API: LLM Proxy Management" +description: "REST API reference for creating, listing, updating, and deleting LLM proxy configurations and API keys in API Platform Gateway." +canonical_url: https://wso2.com/api-platform/docs/api-gateway/gateway-controller-management-api/llm-proxy-management/ +md_url: https://wso2.com/api-platform/docs/api-gateway/gateway-controller-management-api/llm-proxy-management.md +tags: + - api-gateway + - management-api + - llm +author: WSO2 API Platform Documentation Team +last_updated: 2026-06-17 +content_type: "reference" +--- + +# LLM Proxy Management + +CRUD operations for LLM Proxy configurations + +## Create a new LLM proxy + + + +`POST /llm-proxies` + +> Code samples + +```shell + +curl -X POST http://localhost:9090/api/management/v1/llm-proxies \ + -u {username}:{password} \ + -H 'Content-Type: application/json' \ + -H 'Accept: application/json' \ + -d @payload.json + +``` + +Add a new LLM proxy to the Gateway. A proxy defines how to interact with an LLM service deployed in the Gateway, including authentication and policies. + +> Payload + +```json +{ + "apiVersion": "gateway.api-platform.wso2.com/v1", + "kind": "LlmProxy", + "metadata": { + "name": "openai-proxy" + }, + "spec": { + "displayName": "OpenAI Proxy", + "version": "v1.0", + "context": "/openai-proxy", + "provider": { + "id": "wso2-openai-provider" + }, + "policies": [] + } +} +``` + +### Authentication + + + +

Parameters

+ +|Name|In|Type|Required|Description| +|---|---|---|---|---| +|body|body|[LLMProxyConfigurationRequest](schemas.md#schemallmproxyconfigurationrequest)|true|LLM proxy in YAML or JSON format| + +> Example responses + +> 201 Response + +```json +{ + "apiVersion": "gateway.api-platform.wso2.com/v1", + "kind": "LlmProxy", + "metadata": { + "name": "openai-proxy" + }, + "spec": { + "displayName": "OpenAI Proxy", + "version": "v1.0", + "context": "/openai-proxy", + "provider": { + "id": "wso2-openai-provider" + }, + "policies": [] + }, + "status": { + "id": "openai-proxy", + "state": "deployed", + "createdAt": "2026-04-24T07:21:13Z", + "updatedAt": "2026-04-24T07:21:13Z", + "deployedAt": "2026-04-24T07:21:13Z" + } +} +``` + +

Responses

+ +|Status|Meaning|Description|Schema| +|---|---|---|---| +|201|[Created](https://tools.ietf.org/html/rfc7231#section-6.3.2)|LLM proxy created and deployed successfully|[LLMProxyConfiguration](schemas.md#schemallmproxyconfiguration)| +|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Invalid configuration (validation failed)|[ErrorResponse](schemas.md#schemaerrorresponse)| +|409|[Conflict](https://tools.ietf.org/html/rfc7231#section-6.5.8)|Conflict - Proxy with same name and version already exists|[ErrorResponse](schemas.md#schemaerrorresponse)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal server error|[ErrorResponse](schemas.md#schemaerrorresponse)| + +## List all LLM proxies + + + +`GET /llm-proxies` + +> Code samples + +```shell + +curl -X GET http://localhost:9090/api/management/v1/llm-proxies \ + -u {username}:{password} \ + -H 'Accept: application/json' + +``` + +List LLM proxies registered in the Gateway, optionally filtered by name, version, context, status, or vhost. + +### Authentication + + + +

Parameters

+ +|Name|In|Type|Required|Description| +|---|---|---|---|---| +|displayName|query|string|false|Filter by LLM proxy displayName| +|version|query|string|false|Filter by LLM proxy version| +|context|query|string|false|Filter by LLM proxy context/path| +|status|query|string|false|Filter by deployment status| +|vhost|query|string|false|Filter by LLM proxy vhost| + +#### Enumerated Values + +|Parameter|Value| +|---|---| +|status|deployed| +|status|undeployed| + +> Example responses + +> 200 Response + +```json +{ + "status": "success", + "count": 2, + "proxies": [ + { + "apiVersion": "gateway.api-platform.wso2.com/v1", + "kind": "LlmProxy", + "metadata": { + "name": "openai-proxy" + }, + "spec": { + "displayName": "OpenAI Proxy", + "version": "v1.0", + "context": "/openai-proxy", + "provider": { + "id": "wso2-openai-provider" + }, + "policies": [] + }, + "status": { + "id": "openai-proxy", + "state": "deployed", + "createdAt": "2026-04-24T07:21:13Z", + "updatedAt": "2026-04-24T07:21:13Z", + "deployedAt": "2026-04-24T07:21:13Z" + } + } + ] +} +``` + +

Responses

+ +|Status|Meaning|Description|Schema| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|List of LLM proxies|Inline| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal server error|[ErrorResponse](schemas.md#schemaerrorresponse)| + +

Response Schema

+ +Status Code **200** + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|status|string|false|none|none| +|count|integer|false|none|none| +|proxies|[allOf]|false|none|none| + +*allOf* + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|*anonymous*|[LLMProxyConfigurationRequest](schemas.md#schemallmproxyconfigurationrequest)|false|none|none| +|apiVersion|string|true|none|Proxy specification version| +|kind|string|true|none|Proxy kind| +|metadata|[Metadata](schemas.md#schemametadata)|true|none|none| +|name|string|true|none|Unique handle for the resource| +|labels|object|false|none|Labels are key-value pairs for organizing and selecting APIs. Keys must not contain spaces.| +|**additionalProperties**|string|false|none|none| +|annotations|object|false|none|Annotations are arbitrary non-identifying metadata. Use domain-prefixed keys.| +|**additionalProperties**|string|false|none|none| +|spec|[LLMProxyConfigData](schemas.md#schemallmproxyconfigdata)|true|none|none| +|displayName|string|true|none|Human-readable LLM proxy name (must be URL-friendly - only letters, numbers, spaces, hyphens, underscores, and dots allowed)| +|version|string|true|none|Semantic version of the LLM proxy| +|context|string|false|none|Base path for all API routes (must start with /, no trailing slash)| +|vhost|string|false|none|Virtual host name used for routing. Supports standard domain names, subdomains, or wildcard domains. Must follow RFC-compliant hostname rules. Wildcards are only allowed in the left-most label (e.g., *.example.com).| +|provider|[LLMProxyProvider](schemas.md#schemallmproxyprovider)|true|none|none| +|id|string|true|none|Unique id of a deployed llm provider| +|auth|[LLMUpstreamAuth](schemas.md#schemallmupstreamauth)|false|none|none| +|type|string|true|none|none| +|header|string|false|none|none| +|value|string|false|none|none| +|policies|[[LLMPolicy](schemas.md#schemallmpolicy)]|false|none|List of policies applied only to this operation (overrides or adds to API-level policies)| +|name|string|true|none|none| +|version|string|true|none|none| +|paths|[[LLMPolicyPath](schemas.md#schemallmpolicypath)]|true|none|none| +|path|string|true|none|none| +|methods|[string]|true|none|none| +|params|object|true|none|JSON Schema describing the parameters accepted by this policy. This itself is a JSON Schema document.| +|deploymentState|string|false|none|Desired deployment state - 'deployed' (default) or 'undeployed'. When set to 'undeployed', the LLM Proxy is removed from router traffic but configuration and policies are preserved for potential redeployment.| + +*and* + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|*anonymous*|object|false|none|none| +|status|[ResourceStatus](schemas.md#schemaresourcestatus)|false|read-only|Server-managed lifecycle fields. Populated on responses.| +|id|string|false|none|Unique identifier assigned by the server (equal to metadata.name)| +|state|string|false|none|Desired deployment state reported by the server| +|createdAt|string(date-time)|false|none|Timestamp when the resource was first created (UTC)| +|updatedAt|string(date-time)|false|none|Timestamp when the resource was last updated (UTC)| +|deployedAt|string(date-time)|false|none|Timestamp when the resource was last deployed (omitted when undeployed)| + +#### Enumerated Values + +|Property|Value| +|---|---| +|apiVersion|gateway.api-platform.wso2.com/v1| +|kind|LlmProxy| +|type|api-key| +|deploymentState|deployed| +|deploymentState|undeployed| +|state|deployed| +|state|undeployed| + +## Get LLM proxy by unique identifier + + + +`GET /llm-proxies/{id}` + +> Code samples + +```shell + +curl -X GET http://localhost:9090/api/management/v1/llm-proxies/{id} \ + -u {username}:{password} \ + -H 'Accept: application/json' + +``` + +Get an LLM proxy by its ID. + +### Authentication + + + +

Parameters

+ +|Name|In|Type|Required|Description| +|---|---|---|---|---| +|id|path|string|true|Unique identifier of the LLM proxy| + +> Example responses + +> 200 Response + +```json +{ + "apiVersion": "gateway.api-platform.wso2.com/v1", + "kind": "LlmProxy", + "metadata": { + "name": "openai-proxy" + }, + "spec": { + "displayName": "OpenAI Proxy", + "version": "v1.0", + "context": "/openai-proxy", + "provider": { + "id": "wso2-openai-provider" + }, + "policies": [] + }, + "status": { + "id": "openai-proxy", + "state": "deployed", + "createdAt": "2026-04-24T07:21:13Z", + "updatedAt": "2026-04-24T07:21:13Z", + "deployedAt": "2026-04-24T07:21:13Z" + } +} +``` + +

Responses

+ +|Status|Meaning|Description|Schema| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|LLM proxy details|[LLMProxyConfiguration](schemas.md#schemallmproxyconfiguration)| +|404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|LLM proxy not found|[ErrorResponse](schemas.md#schemaerrorresponse)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal server error|[ErrorResponse](schemas.md#schemaerrorresponse)| + +## Update an existing LLM proxy + + + +`PUT /llm-proxies/{id}` + +> Code samples + +```shell + +curl -X PUT http://localhost:9090/api/management/v1/llm-proxies/{id} \ + -u {username}:{password} \ + -H 'Content-Type: application/json' \ + -H 'Accept: application/json' \ + -d @payload.json + +``` + +Update an existing LLM proxy in the Gateway. + +> Payload + +```json +{ + "apiVersion": "gateway.api-platform.wso2.com/v1", + "kind": "LlmProxy", + "metadata": { + "name": "openai-proxy" + }, + "spec": { + "displayName": "OpenAI Proxy", + "version": "v1.0", + "context": "/openai-proxy", + "provider": { + "id": "wso2-openai-provider" + }, + "policies": [] + } +} +``` + +### Authentication + + + +

Parameters

+ +|Name|In|Type|Required|Description| +|---|---|---|---|---| +|id|path|string|true|Unique identifier of the LLM proxy| +|body|body|[LLMProxyConfigurationRequest](schemas.md#schemallmproxyconfigurationrequest)|true|Updated LLM proxy| + +> Example responses + +> 200 Response + +```json +{ + "apiVersion": "gateway.api-platform.wso2.com/v1", + "kind": "LlmProxy", + "metadata": { + "name": "openai-proxy" + }, + "spec": { + "displayName": "OpenAI Proxy", + "version": "v1.0", + "context": "/openai-proxy", + "provider": { + "id": "wso2-openai-provider" + }, + "policies": [] + }, + "status": { + "id": "openai-proxy", + "state": "deployed", + "createdAt": "2026-04-24T07:21:13Z", + "updatedAt": "2026-04-24T07:21:13Z", + "deployedAt": "2026-04-24T07:21:13Z" + } +} +``` + +

Responses

+ +|Status|Meaning|Description|Schema| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|LLM proxy updated successfully|[LLMProxyConfiguration](schemas.md#schemallmproxyconfiguration)| +|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Invalid configuration|[ErrorResponse](schemas.md#schemaerrorresponse)| +|404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|LLM proxy not found|[ErrorResponse](schemas.md#schemaerrorresponse)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal server error|[ErrorResponse](schemas.md#schemaerrorresponse)| + +## Delete an LLM proxy + + + +`DELETE /llm-proxies/{id}` + +> Code samples + +```shell + +curl -X DELETE http://localhost:9090/api/management/v1/llm-proxies/{id} \ + -u {username}:{password} \ + -H 'Accept: application/json' + +``` + +Delete an LLM proxy from the Gateway. + +### Authentication + + + +

Parameters

+ +|Name|In|Type|Required|Description| +|---|---|---|---|---| +|id|path|string|true|Unique identifier of the LLM proxy| + +> Example responses + +> 200 Response + +```json +{ + "status": "success", + "message": "LLM proxy deleted successfully", + "id": "openai-proxy" +} +``` + +

Responses

+ +|Status|Meaning|Description|Schema| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|LLM proxy deleted successfully|Inline| +|404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|LLM proxy not found|[ErrorResponse](schemas.md#schemaerrorresponse)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal server error|[ErrorResponse](schemas.md#schemaerrorresponse)| + +

Response Schema

+ +Status Code **200** + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|status|string|false|none|none| +|message|string|false|none|none| +|id|string|false|none|none| + +## Create a new API key for an LLM proxy + + + +`POST /llm-proxies/{id}/api-keys` + +> Code samples + +```shell + +curl -X POST http://localhost:9090/api/management/v1/llm-proxies/{id}/api-keys \ + -u {username}:{password} \ + -H 'Content-Type: application/json' \ + -H 'Accept: application/json' \ + -d @payload.json + +``` + +Generate a new API key for an LLM proxy in the Gateway. + +> Payload + +```json +{ + "name": "my-production-key" +} +``` + +### Authentication + + + +

Parameters

+ +|Name|In|Type|Required|Description| +|---|---|---|---|---| +|id|path|string|true|Unique handle of the LLM proxy to generate the key for| +|body|body|[APIKeyCreationRequest](schemas.md#schemaapikeycreationrequest)|true|none| + +> Example responses + +> 201 Response + +```json +{ + "status": "success", + "message": "API key generated successfully", + "remainingApiKeyQuota": 9, + "apiKey": { + "name": "my-production-key", + "displayName": "My Production Key", + "apiKey": "apip_1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef", + "apiId": "reading-list-api-v1.0", + "status": "active", + "createdAt": "2026-04-01T10:30:00Z", + "createdBy": "admin", + "expiresAt": null, + "source": "local" + } +} +``` + +

Responses

+ +|Status|Meaning|Description|Schema| +|---|---|---|---| +|201|[Created](https://tools.ietf.org/html/rfc7231#section-6.3.2)|API key created successfully|[APIKeyCreationResponse](schemas.md#schemaapikeycreationresponse)| +|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Invalid configuration (validation failed)|[ErrorResponse](schemas.md#schemaerrorresponse)| +|404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|LLM proxy not found|[ErrorResponse](schemas.md#schemaerrorresponse)| +|409|[Conflict](https://tools.ietf.org/html/rfc7231#section-6.5.8)|Conflict (duplicate key or conflicting update)|[ErrorResponse](schemas.md#schemaerrorresponse)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal server error|[ErrorResponse](schemas.md#schemaerrorresponse)| + +## Get the list of API keys for an LLM proxy + + + +`GET /llm-proxies/{id}/api-keys` + +> Code samples + +```shell + +curl -X GET http://localhost:9090/api/management/v1/llm-proxies/{id}/api-keys \ + -u {username}:{password} \ + -H 'Accept: application/json' + +``` + +List all API keys for an LLM proxy in the Gateway. + +### Authentication + + + +

Parameters

+ +|Name|In|Type|Required|Description| +|---|---|---|---|---| +|id|path|string|true|Unique handle of the LLM proxy to retrieve keys for| + +> Example responses + +> 200 Response + +```json +{ + "apiKeys": [ + { + "name": "my-production-key", + "displayName": "My Production Key", + "apiKey": "apip_1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef", + "apiId": "reading-list-api-v1.0", + "status": "active", + "createdAt": "2026-04-01T10:30:00Z", + "createdBy": "admin", + "expiresAt": null, + "source": "local" + } + ], + "totalCount": 3, + "status": "success" +} +``` + +

Responses

+ +|Status|Meaning|Description|Schema| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|List of API keys|[APIKeyListResponse](schemas.md#schemaapikeylistresponse)| +|404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|LLM proxy not found|[ErrorResponse](schemas.md#schemaerrorresponse)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal server error|[ErrorResponse](schemas.md#schemaerrorresponse)| + +## Regenerate API key for an LLM proxy + + + +`POST /llm-proxies/{id}/api-keys/{apiKeyName}/regenerate` + +> Code samples + +```shell + +curl -X POST http://localhost:9090/api/management/v1/llm-proxies/{id}/api-keys/{apiKeyName}/regenerate \ + -u {username}:{password} \ + -H 'Content-Type: application/json' \ + -H 'Accept: application/json' \ + -d @payload.json + +``` + +Regenerate an existing API key for an LLM proxy in the Gateway. + +> Payload + +```json +{} +``` + +### Authentication + + + +

Parameters

+ +|Name|In|Type|Required|Description| +|---|---|---|---|---| +|id|path|string|true|Unique handle of the LLM proxy| +|apiKeyName|path|string|true|Name of the API key to regenerate| +|body|body|[APIKeyRegenerationRequest](schemas.md#schemaapikeyregenerationrequest)|true|none| + +> Example responses + +> 200 Response + +```json +{ + "status": "success", + "message": "API key generated successfully", + "remainingApiKeyQuota": 9, + "apiKey": { + "name": "my-production-key", + "displayName": "My Production Key", + "apiKey": "apip_1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef", + "apiId": "reading-list-api-v1.0", + "status": "active", + "createdAt": "2026-04-01T10:30:00Z", + "createdBy": "admin", + "expiresAt": null, + "source": "local" + } +} +``` + +

Responses

+ +|Status|Meaning|Description|Schema| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|API key rotated successfully|[APIKeyCreationResponse](schemas.md#schemaapikeycreationresponse)| +|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Invalid configuration (validation failed)|[ErrorResponse](schemas.md#schemaerrorresponse)| +|404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|LLM proxy or API key not found|[ErrorResponse](schemas.md#schemaerrorresponse)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal server error|[ErrorResponse](schemas.md#schemaerrorresponse)| + +## Update an API key for an LLM proxy + + + +`PUT /llm-proxies/{id}/api-keys/{apiKeyName}` + +> Code samples + +```shell + +curl -X PUT http://localhost:9090/api/management/v1/llm-proxies/{id}/api-keys/{apiKeyName} \ + -u {username}:{password} \ + -H 'Content-Type: application/json' \ + -H 'Accept: application/json' \ + -d @payload.json + +``` + +Update an API key with a custom value instead of auto-generating one. + +> Payload + +```json +{ + "name": "my-production-key" +} +``` + +### Authentication + + + +

Parameters

+ +|Name|In|Type|Required|Description| +|---|---|---|---|---| +|id|path|string|true|Unique handle of the LLM proxy| +|apiKeyName|path|string|true|Name of the API key to update| +|body|body|[APIKeyUpdateRequest](schemas.md#schemaapikeyupdaterequest)|true|none| + +> Example responses + +> 200 Response + +```json +{ + "status": "success", + "message": "API key generated successfully", + "remainingApiKeyQuota": 9, + "apiKey": { + "name": "my-production-key", + "displayName": "My Production Key", + "apiKey": "apip_1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef", + "apiId": "reading-list-api-v1.0", + "status": "active", + "createdAt": "2026-04-01T10:30:00Z", + "createdBy": "admin", + "expiresAt": null, + "source": "local" + } +} +``` + +

Responses

+ +|Status|Meaning|Description|Schema| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|API key updated successfully|[APIKeyCreationResponse](schemas.md#schemaapikeycreationresponse)| +|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Invalid request (validation failed)|[ErrorResponse](schemas.md#schemaerrorresponse)| +|404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|LLM proxy or API key not found|[ErrorResponse](schemas.md#schemaerrorresponse)| +|409|[Conflict](https://tools.ietf.org/html/rfc7231#section-6.5.8)|Conflict (duplicate key or conflicting update)|[ErrorResponse](schemas.md#schemaerrorresponse)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal server error|[ErrorResponse](schemas.md#schemaerrorresponse)| + +## Revoke an API key for an LLM proxy + + + +`DELETE /llm-proxies/{id}/api-keys/{apiKeyName}` + +> Code samples + +```shell + +curl -X DELETE http://localhost:9090/api/management/v1/llm-proxies/{id}/api-keys/{apiKeyName} \ + -u {username}:{password} \ + -H 'Accept: application/json' + +``` + +Revoke an API key. Once revoked, it can no longer be used to authenticate requests. + +### Authentication + + + +

Parameters

+ +|Name|In|Type|Required|Description| +|---|---|---|---|---| +|id|path|string|true|Unique handle of the LLM proxy| +|apiKeyName|path|string|true|Name of the API key to revoke| + +> Example responses + +> 200 Response + +```json +{ + "status": "success", + "message": "API key revoked successfully" +} +``` + +

Responses

+ +|Status|Meaning|Description|Schema| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|API key revoked successfully|[APIKeyRevocationResponse](schemas.md#schemaapikeyrevocationresponse)| +|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Invalid configuration (validation failed)|[ErrorResponse](schemas.md#schemaerrorresponse)| +|404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|LLM proxy or API key not found|[ErrorResponse](schemas.md#schemaerrorresponse)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal server error|[ErrorResponse](schemas.md#schemaerrorresponse)| diff --git a/en/docs/api-gateway/1.2.0/gateway-controller-management-api/mcp-proxy-management.md b/en/docs/api-gateway/1.2.0/gateway-controller-management-api/mcp-proxy-management.md new file mode 100644 index 000000000..5fbbcc06c --- /dev/null +++ b/en/docs/api-gateway/1.2.0/gateway-controller-management-api/mcp-proxy-management.md @@ -0,0 +1,579 @@ +--- +title: "Management API: MCP Proxy Management" +description: "REST API reference for creating, listing, updating, and deleting MCP proxy configurations in API Platform Gateway." +canonical_url: https://wso2.com/api-platform/docs/api-gateway/gateway-controller-management-api/mcp-proxy-management/ +md_url: https://wso2.com/api-platform/docs/api-gateway/gateway-controller-management-api/mcp-proxy-management.md +tags: + - api-gateway + - management-api + - mcp +author: WSO2 API Platform Documentation Team +last_updated: 2026-06-17 +content_type: "reference" +--- + +# MCP Proxy Management + +CRUD operations for MCPProxies + +## Create a new MCPProxy + + + +`POST /mcp-proxies` + +> Code samples + +```shell + +curl -X POST http://localhost:9090/api/management/v1/mcp-proxies \ + -u {username}:{password} \ + -H 'Content-Type: application/json' \ + -H 'Accept: application/json' \ + -d @payload.json + +``` + +Add a new MCPProxy to the Gateway. + +> Payload + +```json +{ + "apiVersion": "gateway.api-platform.wso2.com/v1", + "kind": "Mcp", + "metadata": { + "name": "everything-mcp-v1.0" + }, + "spec": { + "displayName": "Everything", + "version": "v1.0", + "context": "/everything", + "specVersion": "2025-06-18", + "upstream": { + "url": "http://everything:3001" + }, + "tools": [], + "resources": [], + "prompts": [] + } +} +``` + +### Authentication + + + +

Parameters

+ +|Name|In|Type|Required|Description| +|---|---|---|---|---| +|body|body|[MCPProxyConfigurationRequest](schemas.md#schemamcpproxyconfigurationrequest)|true|none| + +> Example responses + +> 201 Response + +```json +{ + "apiVersion": "gateway.api-platform.wso2.com/v1", + "kind": "Mcp", + "metadata": { + "name": "everything-mcp-v1.0" + }, + "spec": { + "displayName": "Everything", + "version": "v1.0", + "context": "/everything", + "specVersion": "2025-06-18", + "upstream": { + "url": "http://everything:3001" + }, + "tools": [], + "resources": [], + "prompts": [] + }, + "status": { + "id": "everything-mcp-v1.0", + "state": "deployed", + "createdAt": "2026-04-24T07:21:13Z", + "updatedAt": "2026-04-24T07:21:13Z", + "deployedAt": "2026-04-24T07:21:13Z" + } +} +``` + +

Responses

+ +|Status|Meaning|Description|Schema| +|---|---|---|---| +|201|[Created](https://tools.ietf.org/html/rfc7231#section-6.3.2)|MCPProxy created successfully|[MCPProxyConfiguration](schemas.md#schemamcpproxyconfiguration)| +|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Invalid configuration (validation failed)|[ErrorResponse](schemas.md#schemaerrorresponse)| +|409|[Conflict](https://tools.ietf.org/html/rfc7231#section-6.5.8)|Conflict - MCP Proxy with same name and version already exists|[ErrorResponse](schemas.md#schemaerrorresponse)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal server error|[ErrorResponse](schemas.md#schemaerrorresponse)| + +## List all MCPProxies + + + +`GET /mcp-proxies` + +> Code samples + +```shell + +curl -X GET http://localhost:9090/api/management/v1/mcp-proxies \ + -u {username}:{password} \ + -H 'Accept: application/json' + +``` + +List MCPProxies registered in the Gateway, optionally filtered by name, version, context, or status. + +### Authentication + + + +

Parameters

+ +|Name|In|Type|Required|Description| +|---|---|---|---|---| +|displayName|query|string|false|Filter by MCP proxy display name| +|version|query|string|false|Filter by MCP proxy version| +|context|query|string|false|Filter by MCP proxy context/path| +|status|query|string|false|Filter by deployment status| + +#### Enumerated Values + +|Parameter|Value| +|---|---| +|status|deployed| +|status|undeployed| + +> Example responses + +> 200 Response + +```json +{ + "status": "success", + "count": 5, + "mcpProxies": [ + { + "apiVersion": "gateway.api-platform.wso2.com/v1", + "kind": "Mcp", + "metadata": { + "name": "everything-mcp-v1.0" + }, + "spec": { + "displayName": "Everything", + "version": "v1.0", + "context": "/everything", + "specVersion": "2025-06-18", + "upstream": { + "url": "http://everything:3001" + }, + "tools": [], + "resources": [], + "prompts": [] + }, + "status": { + "id": "everything-mcp-v1.0", + "state": "deployed", + "createdAt": "2026-04-24T07:21:13Z", + "updatedAt": "2026-04-24T07:21:13Z", + "deployedAt": "2026-04-24T07:21:13Z" + } + } + ] +} +``` + +

Responses

+ +|Status|Meaning|Description|Schema| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|List of MCPProxies|Inline| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal server error|[ErrorResponse](schemas.md#schemaerrorresponse)| + +

Response Schema

+ +Status Code **200** + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|status|string|false|none|none| +|count|integer|false|none|none| +|mcpProxies|[allOf]|false|none|none| + +*allOf* + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|*anonymous*|[MCPProxyConfigurationRequest](schemas.md#schemamcpproxyconfigurationrequest)|false|none|none| +|apiVersion|string|true|none|MCP Proxy specification version| +|kind|string|true|none|MCP Proxy type| +|metadata|[Metadata](schemas.md#schemametadata)|true|none|none| +|name|string|true|none|Unique handle for the resource| +|labels|object|false|none|Labels are key-value pairs for organizing and selecting APIs. Keys must not contain spaces.| +|**additionalProperties**|string|false|none|none| +|annotations|object|false|none|Annotations are arbitrary non-identifying metadata. Use domain-prefixed keys.| +|**additionalProperties**|string|false|none|none| +|spec|[MCPProxyConfigData](schemas.md#schemamcpproxyconfigdata)|true|none|none| +|displayName|string|true|none|Human-readable MCP Proxy display name| +|version|string|true|none|MCP Proxy version| +|context|string|false|none|MCP Proxy context path| +|specVersion|string|false|none|MCP specification version| +|vhost|string|false|none|Virtual host name used for routing. Supports standard domain names, subdomains, or wildcard domains. Must follow RFC-compliant hostname rules. Wildcards are only allowed in the left-most label (e.g., *.example.com).| +|upstream|any|true|none|The backend MCP server url and auth configurations| + +*allOf* + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|*anonymous*|[Upstream](schemas.md#schemaupstream)|false|none|Upstream backend configuration (single target or reference)| +|url|string(uri)|false|none|Direct backend URL to route traffic to| +|ref|string|false|none|Reference to a predefined upstreamDefinition| +|hostRewrite|string|false|none|Controls how the Host header is handled when routing to the upstream. `auto` delegates host rewriting to Envoy, which rewrites the Host header using the upstream cluster host. `manual` disables automatic rewriting and expects explicit configuration.| + +*oneOf* + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|*anonymous*|object|false|none|none| + +*xor* + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|*anonymous*|object|false|none|none| + +*and* + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|*anonymous*|[UpstreamAuth](schemas.md#schemaupstreamauth)|false|none|none| +|auth|object|false|none|none| +|type|string|true|none|none| +|header|string|false|none|none| +|value|string|false|none|none| + +*continued* + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|policies|[[Policy](schemas.md#schemapolicy)]|false|none|List of MCP Proxy level policies applied| +|name|string|true|none|Name of the policy| +|version|string|true|none|Version of the policy. Only major-only version is allowed (e.g., v0, v1). Full semantic version (e.g., v1.0.0) is not accepted and will be rejected. The Gateway Controller resolves the major version to the single matching full version installed in the gateway image.| +|executionCondition|string|false|none|Expression controlling conditional execution of the policy| +|params|object|false|none|Arbitrary parameters for the policy (free-form key/value structure)| +|tools|[[MCPTool](schemas.md#schemamcptool)]|false|none|none| +|name|string|true|none|Unique identifier for the tool| +|title|string|false|none|Optional human-readable name of the tool for display purposes.| +|description|string|true|none|Human-readable description of functionality| +|inputSchema|string|true|none|JSON Schema defining expected parameters| +|outputSchema|string|false|none|Optional JSON Schema defining expected output structure| +|resources|[[MCPResource](schemas.md#schemamcpresource)]|false|none|none| +|uri|string|true|none|Unique identifier for the resource| +|name|string|true|none|The name of the resource| +|title|string|false|none|Optional human-readable name of the resource for display purposes| +|description|string|false|none|Optional description| +|mimeType|string|false|none|Optional MIME type| +|size|integer|false|none|Optional size in bytes| +|prompts|[[MCPPrompt](schemas.md#schemamcpprompt)]|false|none|none| +|name|string|true|none|Unique identifier for the prompt| +|title|string|false|none|Optional human-readable name of the prompt for display purposes| +|description|string|false|none|Optional human-readable description| +|arguments|[object]|false|none|Optional list of arguments for customization| +|name|string|true|none|Name of the argument| +|description|string|false|none|Description of the argument| +|required|boolean|false|none|Whether the argument is required| +|title|string|false|none|Optional human-readable title of the argument| +|deploymentState|string|false|none|Desired deployment state - 'deployed' (default) or 'undeployed'. When set to 'undeployed', the MCP Proxy is removed from router traffic but configuration and policies are preserved for potential redeployment.| + +*and* + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|*anonymous*|object|false|none|none| +|status|[ResourceStatus](schemas.md#schemaresourcestatus)|false|read-only|Server-managed lifecycle fields. Populated on responses.| +|id|string|false|none|Unique identifier assigned by the server (equal to metadata.name)| +|state|string|false|none|Desired deployment state reported by the server| +|createdAt|string(date-time)|false|none|Timestamp when the resource was first created (UTC)| +|updatedAt|string(date-time)|false|none|Timestamp when the resource was last updated (UTC)| +|deployedAt|string(date-time)|false|none|Timestamp when the resource was last deployed (omitted when undeployed)| + +#### Enumerated Values + +|Property|Value| +|---|---| +|apiVersion|gateway.api-platform.wso2.com/v1| +|kind|Mcp| +|hostRewrite|auto| +|hostRewrite|manual| +|type|api-key| +|deploymentState|deployed| +|deploymentState|undeployed| +|state|deployed| +|state|undeployed| + +## Get MCPProxy by id + + + +`GET /mcp-proxies/{id}` + +> Code samples + +```shell + +curl -X GET http://localhost:9090/api/management/v1/mcp-proxies/{id} \ + -u {username}:{password} \ + -H 'Accept: application/json' + +``` + +Get an MCPProxy by its ID. + +### Authentication + + + +

Parameters

+ +|Name|In|Type|Required|Description| +|---|---|---|---|---| +|id|path|string|true|Unique public identifier of the MCP Proxy.| + +#### Detailed descriptions + +**id**: Unique public identifier of the MCP Proxy. + +> Example responses + +> 200 Response + +```json +{ + "apiVersion": "gateway.api-platform.wso2.com/v1", + "kind": "Mcp", + "metadata": { + "name": "everything-mcp-v1.0" + }, + "spec": { + "displayName": "Everything", + "version": "v1.0", + "context": "/everything", + "specVersion": "2025-06-18", + "upstream": { + "url": "http://everything:3001" + }, + "tools": [], + "resources": [], + "prompts": [] + }, + "status": { + "id": "everything-mcp-v1.0", + "state": "deployed", + "createdAt": "2026-04-24T07:21:13Z", + "updatedAt": "2026-04-24T07:21:13Z", + "deployedAt": "2026-04-24T07:21:13Z" + } +} +``` + +

Responses

+ +|Status|Meaning|Description|Schema| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|MCPProxy details|[MCPProxyConfiguration](schemas.md#schemamcpproxyconfiguration)| +|404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|MCPProxy not found|[ErrorResponse](schemas.md#schemaerrorresponse)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal server error|[ErrorResponse](schemas.md#schemaerrorresponse)| + +## Update an existing MCPProxy + + + +`PUT /mcp-proxies/{id}` + +> Code samples + +```shell + +curl -X PUT http://localhost:9090/api/management/v1/mcp-proxies/{id} \ + -u {username}:{password} \ + -H 'Content-Type: application/json' \ + -H 'Accept: application/json' \ + -d @payload.json + +``` + +Update an existing MCPProxy in the Gateway. + +> Payload + +```json +{ + "apiVersion": "gateway.api-platform.wso2.com/v1", + "kind": "Mcp", + "metadata": { + "name": "everything-mcp-v1.0" + }, + "spec": { + "displayName": "Everything", + "version": "v1.0", + "context": "/everything", + "specVersion": "2025-06-18", + "upstream": { + "url": "http://everything:3001" + }, + "tools": [], + "resources": [], + "prompts": [] + } +} +``` + +### Authentication + + + +

Parameters

+ +|Name|In|Type|Required|Description| +|---|---|---|---|---| +|id|path|string|true|Unique public identifier of the MCP Proxy to update.| +|body|body|[MCPProxyConfigurationRequest](schemas.md#schemamcpproxyconfigurationrequest)|true|none| + +#### Detailed descriptions + +**id**: Unique public identifier of the MCP Proxy to update. + +> Example responses + +> 200 Response + +```json +{ + "apiVersion": "gateway.api-platform.wso2.com/v1", + "kind": "Mcp", + "metadata": { + "name": "everything-mcp-v1.0" + }, + "spec": { + "displayName": "Everything", + "version": "v1.0", + "context": "/everything", + "specVersion": "2025-06-18", + "upstream": { + "url": "http://everything:3001" + }, + "tools": [], + "resources": [], + "prompts": [] + }, + "status": { + "id": "everything-mcp-v1.0", + "state": "deployed", + "createdAt": "2026-04-24T07:21:13Z", + "updatedAt": "2026-04-24T07:21:13Z", + "deployedAt": "2026-04-24T07:21:13Z" + } +} +``` + +

Responses

+ +|Status|Meaning|Description|Schema| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|MCPProxy updated successfully|[MCPProxyConfiguration](schemas.md#schemamcpproxyconfiguration)| +|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Invalid configuration (validation failed)|[ErrorResponse](schemas.md#schemaerrorresponse)| +|404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|MCPProxy not found|[ErrorResponse](schemas.md#schemaerrorresponse)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal server error|[ErrorResponse](schemas.md#schemaerrorresponse)| + +## Delete a MCPProxy + + + +`DELETE /mcp-proxies/{id}` + +> Code samples + +```shell + +curl -X DELETE http://localhost:9090/api/management/v1/mcp-proxies/{id} \ + -u {username}:{password} \ + -H 'Accept: application/json' + +``` + +Delete an MCPProxy from the Gateway. + +### Authentication + + + +

Parameters

+ +|Name|In|Type|Required|Description| +|---|---|---|---|---| +|id|path|string|true|Unique public identifier of the MCP Proxy to delete.| + +#### Detailed descriptions + +**id**: Unique public identifier of the MCP Proxy to delete. + +> Example responses + +> 200 Response + +```json +{ + "status": "success", + "message": "MCPProxy deleted successfully", + "id": "everything-mcp-v1.0" +} +``` + +

Responses

+ +|Status|Meaning|Description|Schema| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|MCPProxy deleted successfully|Inline| +|404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|MCPProxy not found|[ErrorResponse](schemas.md#schemaerrorresponse)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal server error|[ErrorResponse](schemas.md#schemaerrorresponse)| + +

Response Schema

+ +Status Code **200** + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|status|string|false|none|none| +|message|string|false|none|none| +|id|string|false|none|none| diff --git a/en/docs/api-gateway/1.2.0/gateway-controller-management-api/overview.md b/en/docs/api-gateway/1.2.0/gateway-controller-management-api/overview.md new file mode 100644 index 000000000..577eebd2a --- /dev/null +++ b/en/docs/api-gateway/1.2.0/gateway-controller-management-api/overview.md @@ -0,0 +1,134 @@ +--- +title: "Gateway Controller Management API" +description: "Overview of the API Platform Gateway Controller REST API for managing REST APIs, MCP proxies, LLM providers, certificates, and secrets." +canonical_url: https://wso2.com/api-platform/docs/api-gateway/gateway-controller-management-api/overview/ +md_url: https://wso2.com/api-platform/docs/api-gateway/gateway-controller-management-api/overview.md +tags: + - api-gateway + - management-api +author: WSO2 API Platform Documentation Team +last_updated: 2026-06-17 +content_type: "overview" +--- + +# Gateway Controller Management API v1.0 + +The Gateway Controller Management API is a RESTful API for managing configurations in the WSO2 API Platform Gateway. + +The API exposes the following endpoints: + +* `http://localhost:9090/api/management/v1` +* `http://gateway-controller:9090/api/management/v1` + +## Table of Contents + +### [Authentication](authentication.md) + +- [Overview](authentication.md#overview) +- [How It Works](authentication.md#how-it-works) +- [Configuration](authentication.md#configuration) +- [Role Mapping Semantics](authentication.md#role-mapping-semantics) +- [Troubleshooting (What you’ll observe)](authentication.md#troubleshooting-what-youll-observe) +- [Testing](authentication.md#testing) + +### [Rest API Management](rest-api-management.md) + +- [Create a new RestAPI](rest-api-management.md#create-a-new-restapi) +- [List all RestAPIs](rest-api-management.md#list-all-restapis) +- [Get RestAPI by id](rest-api-management.md#get-restapi-by-id) +- [Update an existing RestAPI](rest-api-management.md#update-an-existing-restapi) +- [Delete a RestAPI](rest-api-management.md#delete-a-restapi) +- [Create a new API key for an API](rest-api-management.md#create-a-new-api-key-for-an-api) +- [Get the list of API keys for an API](rest-api-management.md#get-the-list-of-api-keys-for-an-api) +- [Regenerate API key for an API](rest-api-management.md#regenerate-api-key-for-an-api) +- [Update an API key with a new regenerated value](rest-api-management.md#update-an-api-key-with-a-new-regenerated-value) +- [Revoke an API key](rest-api-management.md#revoke-an-api-key) +- [Create a subscription plan](rest-api-management.md#create-a-subscription-plan) +- [List subscription plans](rest-api-management.md#list-subscription-plans) +- [Get a subscription plan by ID](rest-api-management.md#get-a-subscription-plan-by-id) +- [Update a subscription plan](rest-api-management.md#update-a-subscription-plan) +- [Delete a subscription plan](rest-api-management.md#delete-a-subscription-plan) +- [Create a subscription](rest-api-management.md#create-a-subscription) +- [List subscriptions](rest-api-management.md#list-subscriptions) +- [Get a subscription by ID](rest-api-management.md#get-a-subscription-by-id) +- [Update a subscription](rest-api-management.md#update-a-subscription) +- [Delete a subscription](rest-api-management.md#delete-a-subscription) + +### [MCP Proxy Management](mcp-proxy-management.md) + +- [Create a new MCPProxy](mcp-proxy-management.md#create-a-new-mcpproxy) +- [List all MCPProxies](mcp-proxy-management.md#list-all-mcpproxies) +- [Get MCPProxy by id](mcp-proxy-management.md#get-mcpproxy-by-id) +- [Update an existing MCPProxy](mcp-proxy-management.md#update-an-existing-mcpproxy) +- [Delete a MCPProxy](mcp-proxy-management.md#delete-a-mcpproxy) + +### [Certificate Management](certificate-management.md) + +- [List all custom certificates](certificate-management.md#list-all-custom-certificates) +- [Upload a new certificate](certificate-management.md#upload-a-new-certificate) +- [Delete a certificate](certificate-management.md#delete-a-certificate) +- [Manually reload certificates](certificate-management.md#manually-reload-certificates) + +### [LLM Provider Template Management](llm-provider-template-management.md) + +- [Create a new LLM provider template](llm-provider-template-management.md#create-a-new-llm-provider-template) +- [List all LLM provider templates](llm-provider-template-management.md#list-all-llm-provider-templates) +- [Get LLM provider template by id](llm-provider-template-management.md#get-llm-provider-template-by-id) +- [Update an existing LLM provider template](llm-provider-template-management.md#update-an-existing-llm-provider-template) +- [Delete an LLM provider template](llm-provider-template-management.md#delete-an-llm-provider-template) + +### [LLM Provider Management](llm-provider-management.md) + +- [Create a new LLM provider](llm-provider-management.md#create-a-new-llm-provider) +- [List all LLM providers](llm-provider-management.md#list-all-llm-providers) +- [Get LLM provider by identifier](llm-provider-management.md#get-llm-provider-by-identifier) +- [Update an existing LLM provider](llm-provider-management.md#update-an-existing-llm-provider) +- [Delete an LLM provider](llm-provider-management.md#delete-an-llm-provider) +- [Create a new API key for an LLM provider](llm-provider-management.md#create-a-new-api-key-for-an-llm-provider) +- [Get the list of API keys for an LLM provider](llm-provider-management.md#get-the-list-of-api-keys-for-an-llm-provider) +- [Regenerate API key for an LLM provider](llm-provider-management.md#regenerate-api-key-for-an-llm-provider) +- [Update an API key for an LLM provider](llm-provider-management.md#update-an-api-key-for-an-llm-provider) +- [Revoke an API key for an LLM provider](llm-provider-management.md#revoke-an-api-key-for-an-llm-provider) + +### [LLM Proxy Management](llm-proxy-management.md) + +- [Create a new LLM proxy](llm-proxy-management.md#create-a-new-llm-proxy) +- [List all LLM proxies](llm-proxy-management.md#list-all-llm-proxies) +- [Get LLM proxy by unique identifier](llm-proxy-management.md#get-llm-proxy-by-unique-identifier) +- [Update an existing LLM proxy](llm-proxy-management.md#update-an-existing-llm-proxy) +- [Delete an LLM proxy](llm-proxy-management.md#delete-an-llm-proxy) +- [Create a new API key for an LLM proxy](llm-proxy-management.md#create-a-new-api-key-for-an-llm-proxy) +- [Get the list of API keys for an LLM proxy](llm-proxy-management.md#get-the-list-of-api-keys-for-an-llm-proxy) +- [Regenerate API key for an LLM proxy](llm-proxy-management.md#regenerate-api-key-for-an-llm-proxy) +- [Update an API key for an LLM proxy](llm-proxy-management.md#update-an-api-key-for-an-llm-proxy) +- [Revoke an API key for an LLM proxy](llm-proxy-management.md#revoke-an-api-key-for-an-llm-proxy) + +### [Secrets Management](secrets-management.md) + +- [List all secrets](secrets-management.md#list-all-secrets) +- [Create a new secret](secrets-management.md#create-a-new-secret) +- [Retrieve a secret](secrets-management.md#retrieve-a-secret) +- [Update a secret](secrets-management.md#update-a-secret) +- [Delete a secret](secrets-management.md#delete-a-secret) + +### [WebSub API Management](websub-api-management.md) + +- [Create a new WebSubAPI](websub-api-management.md#create-a-new-websubapi) +- [List all WebSubAPIs](websub-api-management.md#list-all-websubapis) +- [Create a new API key for a WebSub API](websub-api-management.md#create-a-new-api-key-for-a-websub-api) +- [Get the list of API keys for a WebSub API](websub-api-management.md#get-the-list-of-api-keys-for-a-websub-api) +- [Regenerate API key for a WebSub API](websub-api-management.md#regenerate-api-key-for-a-websub-api) +- [Update an API key for a WebSub API](websub-api-management.md#update-an-api-key-for-a-websub-api) +- [Revoke an API key for a WebSub API](websub-api-management.md#revoke-an-api-key-for-a-websub-api) +- [Get WebSubAPI by id](websub-api-management.md#get-websubapi-by-id) +- [Update an existing WebSubAPI](websub-api-management.md#update-an-existing-websubapi) +- [Delete a WebSubAPI](websub-api-management.md#delete-a-websubapi) + +### [WebBroker API Management](webbroker-api-management.md) + +- [Create a new WebBrokerAPI](webbroker-api-management.md#create-a-new-webbrokerapi) +- [List all WebBrokerAPIs](webbroker-api-management.md#list-all-webbrokerapis) +- [Get WebBrokerAPI by id](webbroker-api-management.md#get-webbrokerapi-by-id) +- [Delete a WebBrokerAPI](webbroker-api-management.md#delete-a-webbrokerapi) + +### [Schemas](schemas.md) diff --git a/en/docs/api-gateway/1.2.0/gateway-controller-management-api/rest-api-management.md b/en/docs/api-gateway/1.2.0/gateway-controller-management-api/rest-api-management.md new file mode 100644 index 000000000..d7048f7d5 --- /dev/null +++ b/en/docs/api-gateway/1.2.0/gateway-controller-management-api/rest-api-management.md @@ -0,0 +1,1878 @@ +--- +title: "Management API: REST API Management" +description: "REST API reference for creating, listing, updating, and deleting REST API configurations, API keys, and subscriptions via the Gateway Controller." +canonical_url: https://wso2.com/api-platform/docs/api-gateway/gateway-controller-management-api/rest-api-management/ +md_url: https://wso2.com/api-platform/docs/api-gateway/gateway-controller-management-api/rest-api-management.md +tags: + - api-gateway + - management-api + - rest-api +author: WSO2 API Platform Documentation Team +last_updated: 2026-06-17 +content_type: "reference" +--- + +# Rest API Management + +CRUD operations for Rest APIs + +## Create a new RestAPI + + + +`POST /rest-apis` + +> Code samples + +```shell + +curl -X POST http://localhost:9090/api/management/v1/rest-apis \ + -u {username}:{password} \ + -H 'Content-Type: application/json' \ + -H 'Accept: application/json' \ + -d @payload.json + +``` + +Add a new RestAPI to the Gateway. + +> Payload + +```json +{ + "apiVersion": "gateway.api-platform.wso2.com/v1", + "kind": "RestApi", + "metadata": { + "name": "reading-list-api-v1.0" + }, + "spec": { + "displayName": "Reading-List-API", + "version": "v1.0", + "context": "/reading-list/$version", + "upstream": { + "main": { + "url": "https://apis.bijira.dev/samples/reading-list-api-service/v1.0" + } + }, + "policies": [ + { + "name": "set-headers", + "version": "v1", + "params": { + "request": { + "headers": [ + { + "name": "x-wso2-apip-gateway-version", + "value": "v1.0.0" + } + ] + }, + "response": { + "headers": [ + { + "name": "x-environment", + "value": "development" + } + ] + } + } + } + ], + "operations": [ + { + "method": "GET", + "path": "/books" + }, + { + "method": "POST", + "path": "/books" + }, + { + "method": "GET", + "path": "/books/{id}" + }, + { + "method": "PUT", + "path": "/books/{id}" + }, + { + "method": "DELETE", + "path": "/books/{id}" + } + ] + } +} +``` + +### Authentication + + + +

Parameters

+ +|Name|In|Type|Required|Description| +|---|---|---|---|---| +|body|body|[RestAPIRequest](schemas.md#schemarestapirequest)|true|none| + +> Example responses + +> 201 Response + +```json +{ + "apiVersion": "gateway.api-platform.wso2.com/v1", + "kind": "RestApi", + "metadata": { + "name": "reading-list-api-v1.0" + }, + "spec": { + "displayName": "Reading-List-API", + "version": "v1.0", + "context": "/reading-list/$version", + "upstream": { + "main": { + "url": "https://apis.bijira.dev/samples/reading-list-api-service/v1.0" + } + }, + "policies": [ + { + "name": "set-headers", + "version": "v1", + "params": { + "request": { + "headers": [ + { + "name": "x-wso2-apip-gateway-version", + "value": "v1.0.0" + } + ] + }, + "response": { + "headers": [ + { + "name": "x-environment", + "value": "development" + } + ] + } + } + } + ], + "operations": [ + { + "method": "GET", + "path": "/books" + }, + { + "method": "POST", + "path": "/books" + }, + { + "method": "GET", + "path": "/books/{id}" + }, + { + "method": "PUT", + "path": "/books/{id}" + }, + { + "method": "DELETE", + "path": "/books/{id}" + } + ] + }, + "status": { + "id": "reading-list-api-v1.0", + "state": "deployed", + "createdAt": "2026-04-24T07:21:13Z", + "updatedAt": "2026-04-24T07:21:13Z", + "deployedAt": "2026-04-24T07:21:13Z" + } +} +``` + +

Responses

+ +|Status|Meaning|Description|Schema| +|---|---|---|---| +|201|[Created](https://tools.ietf.org/html/rfc7231#section-6.3.2)|RestAPI created successfully|[RestAPI](schemas.md#schemarestapi)| +|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Invalid configuration (validation failed)|[ErrorResponse](schemas.md#schemaerrorresponse)| +|409|[Conflict](https://tools.ietf.org/html/rfc7231#section-6.5.8)|Conflict - API with same name and version already exists|[ErrorResponse](schemas.md#schemaerrorresponse)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal server error|[ErrorResponse](schemas.md#schemaerrorresponse)| + +## List all RestAPIs + + + +`GET /rest-apis` + +> Code samples + +```shell + +curl -X GET http://localhost:9090/api/management/v1/rest-apis \ + -u {username}:{password} \ + -H 'Accept: application/json' + +``` + +List RestAPIs registered in the Gateway, optionally filtered by name, version, context, or status. + +### Authentication + + + +

Parameters

+ +|Name|In|Type|Required|Description| +|---|---|---|---|---| +|displayName|query|string|false|Filter by API display name| +|version|query|string|false|Filter by API version| +|context|query|string|false|Filter by API context/path| +|status|query|string|false|Filter by deployment status| + +#### Enumerated Values + +|Parameter|Value| +|---|---| +|status|deployed| +|status|undeployed| + +> Example responses + +> 200 Response + +```json +{ + "status": "success", + "count": 5, + "apis": [ + { + "apiVersion": "gateway.api-platform.wso2.com/v1", + "kind": "RestApi", + "metadata": { + "name": "reading-list-api-v1.0" + }, + "spec": { + "displayName": "Reading-List-API", + "version": "v1.0", + "context": "/reading-list/$version", + "upstream": { + "main": { + "url": "https://apis.bijira.dev/samples/reading-list-api-service/v1.0" + } + }, + "policies": [ + { + "name": "set-headers", + "version": "v1", + "params": { + "request": { + "headers": [ + { + "name": "x-wso2-apip-gateway-version", + "value": "v1.0.0" + } + ] + }, + "response": { + "headers": [ + { + "name": "x-environment", + "value": "development" + } + ] + } + } + } + ], + "operations": [ + { + "method": "GET", + "path": "/books" + }, + { + "method": "POST", + "path": "/books" + }, + { + "method": "GET", + "path": "/books/{id}" + }, + { + "method": "PUT", + "path": "/books/{id}" + }, + { + "method": "DELETE", + "path": "/books/{id}" + } + ] + }, + "status": { + "id": "reading-list-api-v1.0", + "state": "deployed", + "createdAt": "2026-04-24T07:21:13Z", + "updatedAt": "2026-04-24T07:21:13Z", + "deployedAt": "2026-04-24T07:21:13Z" + } + } + ] +} +``` + +

Responses

+ +|Status|Meaning|Description|Schema| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|List of RestAPIs|Inline| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal server error|[ErrorResponse](schemas.md#schemaerrorresponse)| + +

Response Schema

+ +Status Code **200** + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|status|string|false|none|none| +|count|integer|false|none|none| +|apis|[allOf]|false|none|none| + +*allOf* + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|*anonymous*|[RestAPIRequest](schemas.md#schemarestapirequest)|false|none|none| +|apiVersion|string|true|none|API specification version| +|kind|string|true|none|API type| +|metadata|[Metadata](schemas.md#schemametadata)|true|none|none| +|name|string|true|none|Unique handle for the resource| +|labels|object|false|none|Labels are key-value pairs for organizing and selecting APIs. Keys must not contain spaces.| +|**additionalProperties**|string|false|none|none| +|annotations|object|false|none|Annotations are arbitrary non-identifying metadata. Use domain-prefixed keys.| +|**additionalProperties**|string|false|none|none| +|spec|[APIConfigData](schemas.md#schemaapiconfigdata)|true|none|none| +|displayName|string|true|none|Human-readable API name (must be URL-friendly - only letters, numbers, spaces, hyphens, underscores, and dots allowed)| +|version|string|true|none|Semantic version of the API| +|context|string|true|none|Base path for all API routes (must start with /, no trailing slash). Use $version to embed the version in the path (e.g., /reading-list/$version resolves to /reading-list/v1.0).| +|upstreamDefinitions|[[UpstreamDefinition](schemas.md#schemaupstreamdefinition)]|false|none|List of reusable upstream definitions with optional timeout configurations| +|name|string|true|none|Unique identifier for this upstream definition| +|basePath|string|false|none|Base path prefix for all endpoints in this upstream (e.g., /api/v2). All requests to this upstream will have this path prepended.| +|timeout|[UpstreamTimeout](schemas.md#schemaupstreamtimeout)|false|none|Timeout configuration for upstream requests| +|connect|string|false|none|Connection timeout duration (e.g., "5s", "500ms")| +|upstreams|[object]|true|none|List of backend targets with optional weights for load balancing| +|url|string(uri)|true|none|Backend URL (host and port only, path comes from basePath)| +|weight|integer|false|none|Weight for load balancing (optional, default 100)| +|upstream|object|true|none|API-level upstream configuration| +|main|[Upstream](schemas.md#schemaupstream)|true|none|Upstream backend configuration (single target or reference)| +|url|string(uri)|false|none|Direct backend URL to route traffic to| +|ref|string|false|none|Reference to a predefined upstreamDefinition| +|hostRewrite|string|false|none|Controls how the Host header is handled when routing to the upstream. `auto` delegates host rewriting to Envoy, which rewrites the Host header using the upstream cluster host. `manual` disables automatic rewriting and expects explicit configuration.| + +*oneOf* + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|*anonymous*|object|false|none|none| + +*xor* + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|*anonymous*|object|false|none|none| + +*continued* + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|sandbox|[Upstream](schemas.md#schemaupstream)|false|none|Upstream backend configuration (single target or reference)| +|vhosts|object|false|none|Custom virtual hosts/domains for the API| +|main|string|true|none|Custom virtual host/domain for production traffic| +|sandbox|string|false|none|Custom virtual host/domain for sandbox traffic| +|subscriptionPlans|[string]|false|none|List of subscription plan names available for this API| +|policies|[[Policy](schemas.md#schemapolicy)]|false|none|List of API-level policies applied to all operations unless overridden| +|name|string|true|none|Name of the policy| +|version|string|true|none|Version of the policy. Only major-only version is allowed (e.g., v0, v1). Full semantic version (e.g., v1.0.0) is not accepted and will be rejected. The Gateway Controller resolves the major version to the single matching full version installed in the gateway image.| +|executionCondition|string|false|none|Expression controlling conditional execution of the policy| +|params|object|false|none|Arbitrary parameters for the policy (free-form key/value structure)| +|operations|[[Operation](schemas.md#schemaoperation)]|true|none|List of HTTP operations/routes| +|method|string|true|none|HTTP method| +|path|string|true|none|Route path with optional {param} placeholders| +|policies|[[Policy](schemas.md#schemapolicy)]|false|none|List of policies applied only to this operation (overrides or adds to API-level policies)| +|deploymentState|string|false|none|Desired deployment state - 'deployed' (default) or 'undeployed'. When set to 'undeployed', the API is removed from router traffic but configuration, API keys, and policies are preserved for potential redeployment.| + +*and* + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|*anonymous*|object|false|none|none| +|status|[ResourceStatus](schemas.md#schemaresourcestatus)|false|read-only|Server-managed lifecycle fields. Populated on responses.| +|id|string|false|none|Unique identifier assigned by the server (equal to metadata.name)| +|state|string|false|none|Desired deployment state reported by the server| +|createdAt|string(date-time)|false|none|Timestamp when the resource was first created (UTC)| +|updatedAt|string(date-time)|false|none|Timestamp when the resource was last updated (UTC)| +|deployedAt|string(date-time)|false|none|Timestamp when the resource was last deployed (omitted when undeployed)| + +#### Enumerated Values + +|Property|Value| +|---|---| +|apiVersion|gateway.api-platform.wso2.com/v1| +|kind|RestApi| +|hostRewrite|auto| +|hostRewrite|manual| +|method|GET| +|method|POST| +|method|PUT| +|method|DELETE| +|method|PATCH| +|method|HEAD| +|method|OPTIONS| +|deploymentState|deployed| +|deploymentState|undeployed| +|state|deployed| +|state|undeployed| + +## Get RestAPI by id + + + +`GET /rest-apis/{id}` + +> Code samples + +```shell + +curl -X GET http://localhost:9090/api/management/v1/rest-apis/{id} \ + -u {username}:{password} \ + -H 'Accept: application/json' + +``` + +Get a RestAPI by its ID. + +### Authentication + + + +

Parameters

+ +|Name|In|Type|Required|Description| +|---|---|---|---|---| +|id|path|string|true|Unique public identifier for the API.| + +#### Detailed descriptions + +**id**: Unique public identifier for the API. + +> Example responses + +> 200 Response + +```json +{ + "apiVersion": "gateway.api-platform.wso2.com/v1", + "kind": "RestApi", + "metadata": { + "name": "reading-list-api-v1.0" + }, + "spec": { + "displayName": "Reading-List-API", + "version": "v1.0", + "context": "/reading-list/$version", + "upstream": { + "main": { + "url": "https://apis.bijira.dev/samples/reading-list-api-service/v1.0" + } + }, + "policies": [ + { + "name": "set-headers", + "version": "v1", + "params": { + "request": { + "headers": [ + { + "name": "x-wso2-apip-gateway-version", + "value": "v1.0.0" + } + ] + }, + "response": { + "headers": [ + { + "name": "x-environment", + "value": "development" + } + ] + } + } + } + ], + "operations": [ + { + "method": "GET", + "path": "/books" + }, + { + "method": "POST", + "path": "/books" + }, + { + "method": "GET", + "path": "/books/{id}" + }, + { + "method": "PUT", + "path": "/books/{id}" + }, + { + "method": "DELETE", + "path": "/books/{id}" + } + ] + }, + "status": { + "id": "reading-list-api-v1.0", + "state": "deployed", + "createdAt": "2026-04-24T07:21:13Z", + "updatedAt": "2026-04-24T07:21:13Z", + "deployedAt": "2026-04-24T07:21:13Z" + } +} +``` + +

Responses

+ +|Status|Meaning|Description|Schema| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|RestAPI details|[RestAPI](schemas.md#schemarestapi)| +|404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|RestAPI not found|[ErrorResponse](schemas.md#schemaerrorresponse)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal server error|[ErrorResponse](schemas.md#schemaerrorresponse)| + +## Update an existing RestAPI + + + +`PUT /rest-apis/{id}` + +> Code samples + +```shell + +curl -X PUT http://localhost:9090/api/management/v1/rest-apis/{id} \ + -u {username}:{password} \ + -H 'Content-Type: application/json' \ + -H 'Accept: application/json' \ + -d @payload.json + +``` + +Update an existing RestAPI in the Gateway. + +> Payload + +```json +{ + "apiVersion": "gateway.api-platform.wso2.com/v1", + "kind": "RestApi", + "metadata": { + "name": "reading-list-api-v1.0" + }, + "spec": { + "displayName": "Reading-List-API", + "version": "v1.0", + "context": "/reading-list/$version", + "upstream": { + "main": { + "url": "https://apis.bijira.dev/samples/reading-list-api-service/v1.0" + } + }, + "policies": [ + { + "name": "set-headers", + "version": "v1", + "params": { + "request": { + "headers": [ + { + "name": "x-wso2-apip-gateway-version", + "value": "v1.0.0" + } + ] + }, + "response": { + "headers": [ + { + "name": "x-environment", + "value": "development" + } + ] + } + } + } + ], + "operations": [ + { + "method": "GET", + "path": "/books" + }, + { + "method": "POST", + "path": "/books" + }, + { + "method": "GET", + "path": "/books/{id}" + }, + { + "method": "PUT", + "path": "/books/{id}" + }, + { + "method": "DELETE", + "path": "/books/{id}" + } + ] + } +} +``` + +### Authentication + + + +

Parameters

+ +|Name|In|Type|Required|Description| +|---|---|---|---|---| +|id|path|string|true|Unique public identifier of the API to update.| +|body|body|[RestAPIRequest](schemas.md#schemarestapirequest)|true|none| + +#### Detailed descriptions + +**id**: Unique public identifier of the API to update. + +> Example responses + +> 200 Response + +```json +{ + "apiVersion": "gateway.api-platform.wso2.com/v1", + "kind": "RestApi", + "metadata": { + "name": "reading-list-api-v1.0" + }, + "spec": { + "displayName": "Reading-List-API", + "version": "v1.0", + "context": "/reading-list/$version", + "upstream": { + "main": { + "url": "https://apis.bijira.dev/samples/reading-list-api-service/v1.0" + } + }, + "policies": [ + { + "name": "set-headers", + "version": "v1", + "params": { + "request": { + "headers": [ + { + "name": "x-wso2-apip-gateway-version", + "value": "v1.0.0" + } + ] + }, + "response": { + "headers": [ + { + "name": "x-environment", + "value": "development" + } + ] + } + } + } + ], + "operations": [ + { + "method": "GET", + "path": "/books" + }, + { + "method": "POST", + "path": "/books" + }, + { + "method": "GET", + "path": "/books/{id}" + }, + { + "method": "PUT", + "path": "/books/{id}" + }, + { + "method": "DELETE", + "path": "/books/{id}" + } + ] + }, + "status": { + "id": "reading-list-api-v1.0", + "state": "deployed", + "createdAt": "2026-04-24T07:21:13Z", + "updatedAt": "2026-04-24T07:21:13Z", + "deployedAt": "2026-04-24T07:21:13Z" + } +} +``` + +

Responses

+ +|Status|Meaning|Description|Schema| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|RestAPI updated successfully|[RestAPI](schemas.md#schemarestapi)| +|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Invalid configuration (validation failed)|[ErrorResponse](schemas.md#schemaerrorresponse)| +|404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|RestAPI not found|[ErrorResponse](schemas.md#schemaerrorresponse)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal server error|[ErrorResponse](schemas.md#schemaerrorresponse)| + +## Delete a RestAPI + + + +`DELETE /rest-apis/{id}` + +> Code samples + +```shell + +curl -X DELETE http://localhost:9090/api/management/v1/rest-apis/{id} \ + -u {username}:{password} \ + -H 'Accept: application/json' + +``` + +Delete a RestAPI from the Gateway. + +### Authentication + + + +

Parameters

+ +|Name|In|Type|Required|Description| +|---|---|---|---|---| +|id|path|string|true|Unique public identifier of the API to delete.| + +#### Detailed descriptions + +**id**: Unique public identifier of the API to delete. + +> Example responses + +> 200 Response + +```json +{ + "status": "success", + "message": "RestAPI deleted successfully", + "id": "reading-list-api-v1.0" +} +``` + +

Responses

+ +|Status|Meaning|Description|Schema| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|RestAPI deleted successfully|Inline| +|404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|RestAPI not found|[ErrorResponse](schemas.md#schemaerrorresponse)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal server error|[ErrorResponse](schemas.md#schemaerrorresponse)| + +

Response Schema

+ +Status Code **200** + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|status|string|false|none|none| +|message|string|false|none|none| +|id|string|false|none|none| + +## Create a new API key for an API + + + +`POST /rest-apis/{id}/api-keys` + +> Code samples + +```shell + +curl -X POST http://localhost:9090/api/management/v1/rest-apis/{id}/api-keys \ + -u {username}:{password} \ + -H 'Content-Type: application/json' \ + -H 'Accept: application/json' \ + -d @payload.json + +``` + +Generate a new API key for a RestAPI in the Gateway. The key is a 32-byte random value encoded in hexadecimal, prefixed with `apip_`. Use the API Key policy on the API to validate incoming requests with this key. + +> Payload + +```json +{ + "name": "my-production-key" +} +``` + +### Authentication + + + +

Parameters

+ +|Name|In|Type|Required|Description| +|---|---|---|---|---| +|id|path|string|true|Unique public identifier of the API to generate the key for| +|body|body|[APIKeyCreationRequest](schemas.md#schemaapikeycreationrequest)|true|none| + +#### Detailed descriptions + +**id**: Unique public identifier of the API to generate the key for + +> Example responses + +> 201 Response + +```json +{ + "status": "success", + "message": "API key generated successfully", + "remainingApiKeyQuota": 9, + "apiKey": { + "name": "my-production-key", + "displayName": "My Production Key", + "apiKey": "apip_1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef", + "apiId": "reading-list-api-v1.0", + "status": "active", + "createdAt": "2026-04-01T10:30:00Z", + "createdBy": "admin", + "expiresAt": null, + "source": "local" + } +} +``` + +

Responses

+ +|Status|Meaning|Description|Schema| +|---|---|---|---| +|201|[Created](https://tools.ietf.org/html/rfc7231#section-6.3.2)|API key created successfully|[APIKeyCreationResponse](schemas.md#schemaapikeycreationresponse)| +|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Invalid configuration (validation failed)|[ErrorResponse](schemas.md#schemaerrorresponse)| +|404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|RestAPI not found|[ErrorResponse](schemas.md#schemaerrorresponse)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal server error|[ErrorResponse](schemas.md#schemaerrorresponse)| + +## Get the list of API keys for an API + + + +`GET /rest-apis/{id}/api-keys` + +> Code samples + +```shell + +curl -X GET http://localhost:9090/api/management/v1/rest-apis/{id}/api-keys \ + -u {username}:{password} \ + -H 'Accept: application/json' + +``` + +List all API keys for a RestAPI in the Gateway. + +### Authentication + + + +

Parameters

+ +|Name|In|Type|Required|Description| +|---|---|---|---|---| +|id|path|string|true|Unique public identifier of the API to retrieve the keys for| + +#### Detailed descriptions + +**id**: Unique public identifier of the API to retrieve the keys for + +> Example responses + +> 200 Response + +```json +{ + "apiKeys": [ + { + "name": "my-production-key", + "displayName": "My Production Key", + "apiKey": "apip_1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef", + "apiId": "reading-list-api-v1.0", + "status": "active", + "createdAt": "2026-04-01T10:30:00Z", + "createdBy": "admin", + "expiresAt": null, + "source": "local" + } + ], + "totalCount": 3, + "status": "success" +} +``` + +

Responses

+ +|Status|Meaning|Description|Schema| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|List of API keys|[APIKeyListResponse](schemas.md#schemaapikeylistresponse)| +|404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|RestAPI not found|[ErrorResponse](schemas.md#schemaerrorresponse)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal server error|[ErrorResponse](schemas.md#schemaerrorresponse)| + +## Regenerate API key for an API + + + +`POST /rest-apis/{id}/api-keys/{apiKeyName}/regenerate` + +> Code samples + +```shell + +curl -X POST http://localhost:9090/api/management/v1/rest-apis/{id}/api-keys/{apiKeyName}/regenerate \ + -u {username}:{password} \ + -H 'Content-Type: application/json' \ + -H 'Accept: application/json' \ + -d @payload.json + +``` + +Regenerate an existing API key for a RestAPI in the Gateway. The previous key is revoked and replaced with a new 32-byte random value encoded in hexadecimal, prefixed with `apip_`. + +> Payload + +```json +{} +``` + +### Authentication + + + +

Parameters

+ +|Name|In|Type|Required|Description| +|---|---|---|---|---| +|id|path|string|true|Unique public identifier of the API to generate the key for| +|apiKeyName|path|string|true|Name of the API key to regenerate| +|body|body|[APIKeyRegenerationRequest](schemas.md#schemaapikeyregenerationrequest)|true|none| + +#### Detailed descriptions + +**id**: Unique public identifier of the API to generate the key for + +**apiKeyName**: Name of the API key to regenerate + +> Example responses + +> 200 Response + +```json +{ + "status": "success", + "message": "API key generated successfully", + "remainingApiKeyQuota": 9, + "apiKey": { + "name": "my-production-key", + "displayName": "My Production Key", + "apiKey": "apip_1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef", + "apiId": "reading-list-api-v1.0", + "status": "active", + "createdAt": "2026-04-01T10:30:00Z", + "createdBy": "admin", + "expiresAt": null, + "source": "local" + } +} +``` + +

Responses

+ +|Status|Meaning|Description|Schema| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|API key rotated successfully|[APIKeyCreationResponse](schemas.md#schemaapikeycreationresponse)| +|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Invalid configuration (validation failed)|[ErrorResponse](schemas.md#schemaerrorresponse)| +|404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|RestAPI not found|[ErrorResponse](schemas.md#schemaerrorresponse)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal server error|[ErrorResponse](schemas.md#schemaerrorresponse)| + +## Update an API key with a new regenerated value + + + +`PUT /rest-apis/{id}/api-keys/{apiKeyName}` + +> Code samples + +```shell + +curl -X PUT http://localhost:9090/api/management/v1/rest-apis/{id}/api-keys/{apiKeyName} \ + -u {username}:{password} \ + -H 'Content-Type: application/json' \ + -H 'Accept: application/json' \ + -d @payload.json + +``` + +Update an API key with a custom value instead of auto-generating one. + +> Payload + +```json +{ + "name": "my-production-key" +} +``` + +### Authentication + + + +

Parameters

+ +|Name|In|Type|Required|Description| +|---|---|---|---|---| +|id|path|string|true|Unique public identifier of the API| +|apiKeyName|path|string|true|Name of the API key to update| +|body|body|[APIKeyUpdateRequest](schemas.md#schemaapikeyupdaterequest)|true|none| + +#### Detailed descriptions + +**id**: Unique public identifier of the API + +**apiKeyName**: Name of the API key to update + +> Example responses + +> 200 Response + +```json +{ + "status": "success", + "message": "API key generated successfully", + "remainingApiKeyQuota": 9, + "apiKey": { + "name": "my-production-key", + "displayName": "My Production Key", + "apiKey": "apip_1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef", + "apiId": "reading-list-api-v1.0", + "status": "active", + "createdAt": "2026-04-01T10:30:00Z", + "createdBy": "admin", + "expiresAt": null, + "source": "local" + } +} +``` + +

Responses

+ +|Status|Meaning|Description|Schema| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|API key updated successfully|[APIKeyCreationResponse](schemas.md#schemaapikeycreationresponse)| +|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Invalid request (validation failed)|[ErrorResponse](schemas.md#schemaerrorresponse)| +|404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|API or API key not found|[ErrorResponse](schemas.md#schemaerrorresponse)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal server error|[ErrorResponse](schemas.md#schemaerrorresponse)| + +## Revoke an API key + + + +`DELETE /rest-apis/{id}/api-keys/{apiKeyName}` + +> Code samples + +```shell + +curl -X DELETE http://localhost:9090/api/management/v1/rest-apis/{id}/api-keys/{apiKeyName} \ + -u {username}:{password} \ + -H 'Accept: application/json' + +``` + +Revoke an API key. Once revoked, it can no longer be used to authenticate requests. + +### Authentication + + + +

Parameters

+ +|Name|In|Type|Required|Description| +|---|---|---|---|---| +|id|path|string|true|Unique public identifier of the API to revoke the key for| +|apiKeyName|path|string|true|Name of the API key to revoke| + +#### Detailed descriptions + +**id**: Unique public identifier of the API to revoke the key for + +**apiKeyName**: Name of the API key to revoke + +> Example responses + +> 200 Response + +```json +{ + "status": "success", + "message": "API key revoked successfully" +} +``` + +

Responses

+ +|Status|Meaning|Description|Schema| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|API key revoked successfully|[APIKeyRevocationResponse](schemas.md#schemaapikeyrevocationresponse)| +|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Invalid configuration (validation failed)|[ErrorResponse](schemas.md#schemaerrorresponse)| +|404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|RestAPI not found|[ErrorResponse](schemas.md#schemaerrorresponse)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal server error|[ErrorResponse](schemas.md#schemaerrorresponse)| + +## Create a subscription plan + + + +`POST /subscription-plans` + +> Code samples + +```shell + +curl -X POST http://localhost:9090/api/management/v1/subscription-plans \ + -u {username}:{password} \ + -H 'Content-Type: application/json' \ + -H 'Accept: application/json' \ + -d @payload.json + +``` + +Create a subscription plan that defines rate limits and access tiers for API subscriptions. + +> Payload + +```json +{ + "planName": "Gold", + "billingPlan": "COMMERCIAL", + "stopOnQuotaReach": true, + "throttleLimitCount": 1000, + "throttleLimitUnit": "Hour", + "expiryTime": "2026-12-31T23:59:59Z", + "status": "ACTIVE" +} +``` + +### Authentication + + + +

Parameters

+ +|Name|In|Type|Required|Description| +|---|---|---|---|---| +|body|body|[SubscriptionPlanCreateRequest](schemas.md#schemasubscriptionplancreaterequest)|true|none| + +> Example responses + +> 201 Response + +```json +{ + "id": "string", + "planName": "string", + "billingPlan": "string", + "stopOnQuotaReach": true, + "throttleLimitCount": 0, + "throttleLimitUnit": "string", + "expiryTime": "2019-08-24T14:15:22Z", + "gatewayId": "string", + "status": "ACTIVE", + "createdAt": "2019-08-24T14:15:22Z", + "updatedAt": "2019-08-24T14:15:22Z" +} +``` + +

Responses

+ +|Status|Meaning|Description|Schema| +|---|---|---|---| +|201|[Created](https://tools.ietf.org/html/rfc7231#section-6.3.2)|Subscription plan created|[SubscriptionPlanResponse](schemas.md#schemasubscriptionplanresponse)| +|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad request|[ErrorResponse](schemas.md#schemaerrorresponse)| +|409|[Conflict](https://tools.ietf.org/html/rfc7231#section-6.5.8)|Conflict|[ErrorResponse](schemas.md#schemaerrorresponse)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal server error|[ErrorResponse](schemas.md#schemaerrorresponse)| + +## List subscription plans + + + +`GET /subscription-plans` + +> Code samples + +```shell + +curl -X GET http://localhost:9090/api/management/v1/subscription-plans \ + -u {username}:{password} \ + -H 'Accept: application/json' + +``` + +List all subscription plans available in the Gateway. + +### Authentication + + + +> Example responses + +> 200 Response + +```json +{ + "subscriptionPlans": [ + { + "id": "string", + "planName": "string", + "billingPlan": "string", + "stopOnQuotaReach": true, + "throttleLimitCount": 0, + "throttleLimitUnit": "string", + "expiryTime": "2019-08-24T14:15:22Z", + "gatewayId": "string", + "status": "ACTIVE", + "createdAt": "2019-08-24T14:15:22Z", + "updatedAt": "2019-08-24T14:15:22Z" + } + ], + "count": 0 +} +``` + +

Responses

+ +|Status|Meaning|Description|Schema| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|List of subscription plans|[SubscriptionPlanListResponse](schemas.md#schemasubscriptionplanlistresponse)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal server error|[ErrorResponse](schemas.md#schemaerrorresponse)| + +## Get a subscription plan by ID + + + +`GET /subscription-plans/{planId}` + +> Code samples + +```shell + +curl -X GET http://localhost:9090/api/management/v1/subscription-plans/{planId} \ + -u {username}:{password} \ + -H 'Accept: application/json' + +``` + +Get the details of a subscription plan by its ID. + +### Authentication + + + +

Parameters

+ +|Name|In|Type|Required|Description| +|---|---|---|---|---| +|planId|path|string|true|none| + +> Example responses + +> 200 Response + +```json +{ + "id": "string", + "planName": "string", + "billingPlan": "string", + "stopOnQuotaReach": true, + "throttleLimitCount": 0, + "throttleLimitUnit": "string", + "expiryTime": "2019-08-24T14:15:22Z", + "gatewayId": "string", + "status": "ACTIVE", + "createdAt": "2019-08-24T14:15:22Z", + "updatedAt": "2019-08-24T14:15:22Z" +} +``` + +

Responses

+ +|Status|Meaning|Description|Schema| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|Subscription plan details|[SubscriptionPlanResponse](schemas.md#schemasubscriptionplanresponse)| +|404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Not found|[ErrorResponse](schemas.md#schemaerrorresponse)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal server error|[ErrorResponse](schemas.md#schemaerrorresponse)| + +## Update a subscription plan + + + +`PUT /subscription-plans/{planId}` + +> Code samples + +```shell + +curl -X PUT http://localhost:9090/api/management/v1/subscription-plans/{planId} \ + -u {username}:{password} \ + -H 'Content-Type: application/json' \ + -H 'Accept: application/json' \ + -d @payload.json + +``` + +Update an existing subscription plan in the Gateway. + +> Payload + +```json +{ + "planName": "string", + "billingPlan": "string", + "stopOnQuotaReach": true, + "throttleLimitCount": 0, + "throttleLimitUnit": "Min", + "expiryTime": "2019-08-24T14:15:22Z", + "status": "ACTIVE" +} +``` + +### Authentication + + + +

Parameters

+ +|Name|In|Type|Required|Description| +|---|---|---|---|---| +|planId|path|string|true|none| +|body|body|[SubscriptionPlanUpdateRequest](schemas.md#schemasubscriptionplanupdaterequest)|false|none| + +> Example responses + +> 200 Response + +```json +{ + "id": "string", + "planName": "string", + "billingPlan": "string", + "stopOnQuotaReach": true, + "throttleLimitCount": 0, + "throttleLimitUnit": "string", + "expiryTime": "2019-08-24T14:15:22Z", + "gatewayId": "string", + "status": "ACTIVE", + "createdAt": "2019-08-24T14:15:22Z", + "updatedAt": "2019-08-24T14:15:22Z" +} +``` + +

Responses

+ +|Status|Meaning|Description|Schema| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|Subscription plan updated|[SubscriptionPlanResponse](schemas.md#schemasubscriptionplanresponse)| +|404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Not found|[ErrorResponse](schemas.md#schemaerrorresponse)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal server error|[ErrorResponse](schemas.md#schemaerrorresponse)| + +## Delete a subscription plan + + + +`DELETE /subscription-plans/{planId}` + +> Code samples + +```shell + +curl -X DELETE http://localhost:9090/api/management/v1/subscription-plans/{planId} \ + -u {username}:{password} \ + -H 'Accept: application/json' + +``` + +Delete a subscription plan from the Gateway. + +### Authentication + + + +

Parameters

+ +|Name|In|Type|Required|Description| +|---|---|---|---|---| +|planId|path|string|true|none| + +> Example responses + +> 404 Response + +```json +{ + "status": "error", + "message": "Configuration validation failed", + "errors": [ + { + "field": "spec.context", + "message": "Context must start with / and cannot end with /" + } + ] +} +``` + +

Responses

+ +|Status|Meaning|Description|Schema| +|---|---|---|---| +|204|[No Content](https://tools.ietf.org/html/rfc7231#section-6.3.5)|Subscription plan deleted|None| +|404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Not found|[ErrorResponse](schemas.md#schemaerrorresponse)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal server error|[ErrorResponse](schemas.md#schemaerrorresponse)| + +## Create a subscription + + + +`POST /subscriptions` + +> Code samples + +```shell + +curl -X POST http://localhost:9090/api/management/v1/subscriptions \ + -u {username}:{password} \ + -H 'Content-Type: application/json' \ + -H 'Accept: application/json' \ + -d @payload.json + +``` + +Subscribe an application to a RestAPI in the Gateway. + +> Payload + +```json +{ + "apiId": "c9f2b6ae-1234-5678-9abc-def012345678", + "subscriptionToken": "sub-token-abc123xyz", + "applicationId": "string", + "subscriptionPlanId": "string", + "billingCustomerId": "string", + "billingSubscriptionId": "string", + "status": "ACTIVE" +} +``` + +### Authentication + + + +

Parameters

+ +|Name|In|Type|Required|Description| +|---|---|---|---|---| +|body|body|[SubscriptionCreateRequest](schemas.md#schemasubscriptioncreaterequest)|true|none| + +> Example responses + +> 201 Response + +```json +{ + "id": "string", + "apiId": "string", + "applicationId": "string", + "subscriptionToken": "string", + "subscriptionPlanId": "string", + "billingCustomerId": "string", + "billingSubscriptionId": "string", + "gatewayId": "string", + "status": "ACTIVE", + "createdAt": "2019-08-24T14:15:22Z", + "updatedAt": "2019-08-24T14:15:22Z" +} +``` + +

Responses

+ +|Status|Meaning|Description|Schema| +|---|---|---|---| +|201|[Created](https://tools.ietf.org/html/rfc7231#section-6.3.2)|Subscription created|[SubscriptionResponse](schemas.md#schemasubscriptionresponse)| +|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad request|[ErrorResponse](schemas.md#schemaerrorresponse)| +|409|[Conflict](https://tools.ietf.org/html/rfc7231#section-6.5.8)|Conflict - subscription already exists|[ErrorResponse](schemas.md#schemaerrorresponse)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal server error|[ErrorResponse](schemas.md#schemaerrorresponse)| + +## List subscriptions + + + +`GET /subscriptions` + +> Code samples + +```shell + +curl -X GET http://localhost:9090/api/management/v1/subscriptions \ + -u {username}:{password} \ + -H 'Accept: application/json' + +``` + +List subscriptions in the Gateway, optionally filtered by API, application, or status. + +### Authentication + + + +

Parameters

+ +|Name|In|Type|Required|Description| +|---|---|---|---|---| +|apiId|query|string|false|Filter by API ID (deployment ID or handle)| +|applicationId|query|string|false|none| +|status|query|string|false|none| + +#### Enumerated Values + +|Parameter|Value| +|---|---| +|status|ACTIVE| +|status|INACTIVE| +|status|REVOKED| + +> Example responses + +> 200 Response + +```json +{ + "subscriptions": [ + { + "id": "string", + "apiId": "string", + "applicationId": "string", + "subscriptionToken": "string", + "subscriptionPlanId": "string", + "billingCustomerId": "string", + "billingSubscriptionId": "string", + "gatewayId": "string", + "status": "ACTIVE", + "createdAt": "2019-08-24T14:15:22Z", + "updatedAt": "2019-08-24T14:15:22Z" + } + ], + "count": 0 +} +``` + +

Responses

+ +|Status|Meaning|Description|Schema| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|List of subscriptions|[SubscriptionListResponse](schemas.md#schemasubscriptionlistresponse)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal server error|[ErrorResponse](schemas.md#schemaerrorresponse)| + +## Get a subscription by ID + + + +`GET /subscriptions/{subscriptionId}` + +> Code samples + +```shell + +curl -X GET http://localhost:9090/api/management/v1/subscriptions/{subscriptionId} \ + -u {username}:{password} \ + -H 'Accept: application/json' + +``` + +Get the details of a subscription by its ID. + +### Authentication + + + +

Parameters

+ +|Name|In|Type|Required|Description| +|---|---|---|---|---| +|subscriptionId|path|string|true|none| + +> Example responses + +> 200 Response + +```json +{ + "id": "string", + "apiId": "string", + "applicationId": "string", + "subscriptionToken": "string", + "subscriptionPlanId": "string", + "billingCustomerId": "string", + "billingSubscriptionId": "string", + "gatewayId": "string", + "status": "ACTIVE", + "createdAt": "2019-08-24T14:15:22Z", + "updatedAt": "2019-08-24T14:15:22Z" +} +``` + +

Responses

+ +|Status|Meaning|Description|Schema| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|Subscription details|[SubscriptionResponse](schemas.md#schemasubscriptionresponse)| +|404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Subscription not found|[ErrorResponse](schemas.md#schemaerrorresponse)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal server error|[ErrorResponse](schemas.md#schemaerrorresponse)| + +## Update a subscription + + + +`PUT /subscriptions/{subscriptionId}` + +> Code samples + +```shell + +curl -X PUT http://localhost:9090/api/management/v1/subscriptions/{subscriptionId} \ + -u {username}:{password} \ + -H 'Content-Type: application/json' \ + -H 'Accept: application/json' \ + -d @payload.json + +``` + +Update an existing subscription in the Gateway. + +> Payload + +```json +{ + "status": "ACTIVE" +} +``` + +### Authentication + + + +

Parameters

+ +|Name|In|Type|Required|Description| +|---|---|---|---|---| +|subscriptionId|path|string|true|none| +|body|body|[SubscriptionUpdateRequest](schemas.md#schemasubscriptionupdaterequest)|false|none| + +> Example responses + +> 200 Response + +```json +{ + "id": "string", + "apiId": "string", + "applicationId": "string", + "subscriptionToken": "string", + "subscriptionPlanId": "string", + "billingCustomerId": "string", + "billingSubscriptionId": "string", + "gatewayId": "string", + "status": "ACTIVE", + "createdAt": "2019-08-24T14:15:22Z", + "updatedAt": "2019-08-24T14:15:22Z" +} +``` + +

Responses

+ +|Status|Meaning|Description|Schema| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|Subscription updated|[SubscriptionResponse](schemas.md#schemasubscriptionresponse)| +|404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Subscription not found|[ErrorResponse](schemas.md#schemaerrorresponse)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal server error|[ErrorResponse](schemas.md#schemaerrorresponse)| + +## Delete a subscription + + + +`DELETE /subscriptions/{subscriptionId}` + +> Code samples + +```shell + +curl -X DELETE http://localhost:9090/api/management/v1/subscriptions/{subscriptionId} \ + -u {username}:{password} \ + -H 'Accept: application/json' + +``` + +Delete a subscription from the Gateway. + +### Authentication + + + +

Parameters

+ +|Name|In|Type|Required|Description| +|---|---|---|---|---| +|subscriptionId|path|string|true|none| + +> Example responses + +> 404 Response + +```json +{ + "status": "error", + "message": "Configuration validation failed", + "errors": [ + { + "field": "spec.context", + "message": "Context must start with / and cannot end with /" + } + ] +} +``` + +

Responses

+ +|Status|Meaning|Description|Schema| +|---|---|---|---| +|204|[No Content](https://tools.ietf.org/html/rfc7231#section-6.3.5)|Subscription deleted|None| +|404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Subscription not found|[ErrorResponse](schemas.md#schemaerrorresponse)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal server error|[ErrorResponse](schemas.md#schemaerrorresponse)| diff --git a/en/docs/api-gateway/1.2.0/gateway-controller-management-api/schemas.md b/en/docs/api-gateway/1.2.0/gateway-controller-management-api/schemas.md new file mode 100644 index 000000000..a6ee781fb --- /dev/null +++ b/en/docs/api-gateway/1.2.0/gateway-controller-management-api/schemas.md @@ -0,0 +1,3936 @@ +--- +title: "Management API Schemas" +description: "JSON schema definitions for all API Platform Gateway Controller management API request and response objects." +canonical_url: https://wso2.com/api-platform/docs/api-gateway/gateway-controller-management-api/schemas/ +md_url: https://wso2.com/api-platform/docs/api-gateway/gateway-controller-management-api/schemas.md +tags: + - api-gateway + - management-api +author: WSO2 API Platform Documentation Team +last_updated: 2026-06-17 +content_type: "reference" +--- + +# Schemas + +

ResourceStatus

+ + + + + + +```json +{ + "id": "reading-list-api-v1.0", + "state": "deployed", + "createdAt": "2026-04-24T07:21:13Z", + "updatedAt": "2026-04-24T07:21:13Z", + "deployedAt": "2026-04-24T07:21:13Z" +} + +``` + +Server-managed lifecycle information for a resource + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|id|string|false|none|Unique identifier assigned by the server (equal to metadata.name)| +|state|string|false|none|Desired deployment state reported by the server| +|createdAt|string(date-time)|false|none|Timestamp when the resource was first created (UTC)| +|updatedAt|string(date-time)|false|none|Timestamp when the resource was last updated (UTC)| +|deployedAt|string(date-time)|false|none|Timestamp when the resource was last deployed (omitted when undeployed)| + +#### Enumerated Values + +|Property|Value| +|---|---| +|state|deployed| +|state|undeployed| + +

RestAPIRequest

+ + + + + + +```json +{ + "apiVersion": "gateway.api-platform.wso2.com/v1", + "kind": "RestApi", + "metadata": { + "name": "reading-list-api-v1.0" + }, + "spec": { + "displayName": "Reading-List-API", + "version": "v1.0", + "context": "/reading-list/$version", + "upstream": { + "main": { + "url": "https://apis.bijira.dev/samples/reading-list-api-service/v1.0" + } + }, + "policies": [ + { + "name": "set-headers", + "version": "v1", + "params": { + "request": { + "headers": [ + { + "name": "x-wso2-apip-gateway-version", + "value": "v1.0.0" + } + ] + }, + "response": { + "headers": [ + { + "name": "x-environment", + "value": "development" + } + ] + } + } + } + ], + "operations": [ + { + "method": "GET", + "path": "/books" + }, + { + "method": "POST", + "path": "/books" + }, + { + "method": "GET", + "path": "/books/{id}" + }, + { + "method": "PUT", + "path": "/books/{id}" + }, + { + "method": "DELETE", + "path": "/books/{id}" + } + ] + } +} + +``` + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|apiVersion|string|true|none|API specification version| +|kind|string|true|none|API type| +|metadata|[Metadata](#schemametadata)|true|none|none| +|spec|[APIConfigData](#schemaapiconfigdata)|true|none|none| + +#### Enumerated Values + +|Property|Value| +|---|---| +|apiVersion|gateway.api-platform.wso2.com/v1| +|kind|RestApi| + +

RestAPI

+ + + + + + +```json +{ + "apiVersion": "gateway.api-platform.wso2.com/v1", + "kind": "RestApi", + "metadata": { + "name": "reading-list-api-v1.0" + }, + "spec": { + "displayName": "Reading-List-API", + "version": "v1.0", + "context": "/reading-list/$version", + "upstream": { + "main": { + "url": "https://apis.bijira.dev/samples/reading-list-api-service/v1.0" + } + }, + "policies": [ + { + "name": "set-headers", + "version": "v1", + "params": { + "request": { + "headers": [ + { + "name": "x-wso2-apip-gateway-version", + "value": "v1.0.0" + } + ] + }, + "response": { + "headers": [ + { + "name": "x-environment", + "value": "development" + } + ] + } + } + } + ], + "operations": [ + { + "method": "GET", + "path": "/books" + }, + { + "method": "POST", + "path": "/books" + }, + { + "method": "GET", + "path": "/books/{id}" + }, + { + "method": "PUT", + "path": "/books/{id}" + }, + { + "method": "DELETE", + "path": "/books/{id}" + } + ] + }, + "status": { + "id": "reading-list-api-v1.0", + "state": "deployed", + "createdAt": "2026-04-24T07:21:13Z", + "updatedAt": "2026-04-24T07:21:13Z", + "deployedAt": "2026-04-24T07:21:13Z" + } +} + +``` + +### Properties + +allOf + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|*anonymous*|[RestAPIRequest](#schemarestapirequest)|false|none|none| + +and + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|*anonymous*|object|false|none|none| +|status|[ResourceStatus](#schemaresourcestatus)|false|read-only|Server-managed lifecycle fields. Populated on responses.| + +

WebSubAPIRequest

+ + + + + + +```json +{ + "apiVersion": "gateway.api-platform.wso2.com/v1", + "kind": "WebSubApi", + "metadata": { + "name": "github-events-v1.0" + }, + "spec": { + "displayName": "GitHub Events", + "version": "v1.0", + "context": "/github-events/$version", + "channels": [ + { + "name": "issues", + "method": "SUB" + }, + { + "name": "pull_requests", + "method": "SUB" + } + ] + } +} + +``` + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|apiVersion|string|true|none|API specification version| +|kind|string|true|none|API type| +|metadata|[Metadata](#schemametadata)|true|none|none| +|spec|[WebhookAPIData](#schemawebhookapidata)|true|none|none| + +#### Enumerated Values + +|Property|Value| +|---|---| +|apiVersion|gateway.api-platform.wso2.com/v1| +|kind|WebSubApi| + +

WebSubAPI

+ + + + + + +```json +{ + "apiVersion": "gateway.api-platform.wso2.com/v1", + "kind": "WebSubApi", + "metadata": { + "name": "github-events-v1.0" + }, + "spec": { + "displayName": "GitHub Events", + "version": "v1.0", + "context": "/github-events/$version", + "channels": [ + { + "name": "issues", + "method": "SUB" + }, + { + "name": "pull_requests", + "method": "SUB" + } + ] + }, + "status": { + "id": "github-events-v1.0", + "state": "deployed", + "createdAt": "2026-04-24T07:21:13Z", + "updatedAt": "2026-04-24T07:21:13Z", + "deployedAt": "2026-04-24T07:21:13Z" + } +} + +``` + +### Properties + +allOf + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|*anonymous*|[WebSubAPIRequest](#schemawebsubapirequest)|false|none|none| + +and + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|*anonymous*|object|false|none|none| +|status|[ResourceStatus](#schemaresourcestatus)|false|read-only|Server-managed lifecycle fields. Populated on responses.| + +

Metadata

+ + + + + + +```json +{ + "name": "reading-list-api-v1.0", + "labels": { + "environment": "production", + "team": "backend", + "version": "v1" + }, + "annotations": { + "gateway.api-platform.wso2.com/project-id": "019d953f-d386-7a64-aa92-1869a28292e0" + } +} + +``` + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|name|string|true|none|Unique handle for the resource| +|labels|object|false|none|Labels are key-value pairs for organizing and selecting APIs. Keys must not contain spaces.| +|**additionalProperties**|string|false|none|none| +|annotations|object|false|none|Annotations are arbitrary non-identifying metadata. Use domain-prefixed keys.| +|**additionalProperties**|string|false|none|none| + +

APIConfigData

+ + + + + + +```json +{ + "displayName": "Reading List API", + "version": "v1.0", + "context": "/reading-list/$version", + "upstreamDefinitions": [ + { + "name": "my-upstream-1", + "basePath": "/api/v2", + "timeout": { + "connect": "5s" + }, + "upstreams": [ + { + "url": "http://prod-backend-1:5000", + "weight": 80 + } + ] + } + ], + "upstream": { + "main": { + "url": "http://prod-backend:5000/api/v2", + "ref": "string", + "hostRewrite": "auto" + }, + "sandbox": { + "url": "http://prod-backend:5000/api/v2", + "ref": "string", + "hostRewrite": "auto" + } + }, + "vhosts": { + "main": "api.example.com", + "sandbox": "sandbox-api.example.com" + }, + "subscriptionPlans": [ + "Gold", + "Silver" + ], + "policies": [ + { + "name": "cors", + "version": "v1", + "executionCondition": "request.metadata[authenticated] != true", + "params": {} + } + ], + "operations": [ + { + "method": "GET", + "path": "/books/{id}", + "policies": [ + { + "name": "cors", + "version": "v1", + "executionCondition": "request.metadata[authenticated] != true", + "params": {} + } + ] + } + ], + "deploymentState": "deployed" +} + +``` + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|displayName|string|true|none|Human-readable API name (must be URL-friendly - only letters, numbers, spaces, hyphens, underscores, and dots allowed)| +|version|string|true|none|Semantic version of the API| +|context|string|true|none|Base path for all API routes (must start with /, no trailing slash). Use $version to embed the version in the path (e.g., /reading-list/$version resolves to /reading-list/v1.0).| +|upstreamDefinitions|[[UpstreamDefinition](#schemaupstreamdefinition)]|false|none|List of reusable upstream definitions with optional timeout configurations| +|upstream|object|true|none|API-level upstream configuration| +|main|[Upstream](#schemaupstream)|true|none|Upstream backend configuration (single target or reference)| +|sandbox|[Upstream](#schemaupstream)|false|none|Upstream backend configuration (single target or reference)| +|vhosts|object|false|none|Custom virtual hosts/domains for the API| +|main|string|true|none|Custom virtual host/domain for production traffic| +|sandbox|string|false|none|Custom virtual host/domain for sandbox traffic| +|subscriptionPlans|[string]|false|none|List of subscription plan names available for this API| +|policies|[[Policy](#schemapolicy)]|false|none|List of API-level policies applied to all operations unless overridden| +|operations|[[Operation](#schemaoperation)]|true|none|List of HTTP operations/routes| +|deploymentState|string|false|none|Desired deployment state - 'deployed' (default) or 'undeployed'. When set to 'undeployed', the API is removed from router traffic but configuration, API keys, and policies are preserved for potential redeployment.| + +#### Enumerated Values + +|Property|Value| +|---|---| +|deploymentState|deployed| +|deploymentState|undeployed| + +

UpstreamDefinition

+ + + + + + +```json +{ + "name": "my-upstream-1", + "basePath": "/api/v2", + "timeout": { + "connect": "5s" + }, + "upstreams": [ + { + "url": "http://prod-backend-1:5000", + "weight": 80 + } + ] +} + +``` + +Reusable upstream configuration with optional timeout and load balancing settings + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|name|string|true|none|Unique identifier for this upstream definition| +|basePath|string|false|none|Base path prefix for all endpoints in this upstream (e.g., /api/v2). All requests to this upstream will have this path prepended.| +|timeout|[UpstreamTimeout](#schemaupstreamtimeout)|false|none|Timeout configuration for upstream requests| +|upstreams|[object]|true|none|List of backend targets with optional weights for load balancing| +|url|string(uri)|true|none|Backend URL (host and port only, path comes from basePath)| +|weight|integer|false|none|Weight for load balancing (optional, default 100)| + +

UpstreamTimeout

+ + + + + + +```json +{ + "connect": "5s" +} + +``` + +Timeout configuration for upstream requests + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|connect|string|false|none|Connection timeout duration (e.g., "5s", "500ms")| + +

Upstream

+ + + + + + +```json +{ + "url": "http://prod-backend:5000/api/v2", + "ref": "string", + "hostRewrite": "auto" +} + +``` + +Upstream backend configuration (single target or reference) + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|url|string(uri)|false|none|Direct backend URL to route traffic to| +|ref|string|false|none|Reference to a predefined upstreamDefinition| +|hostRewrite|string|false|none|Controls how the Host header is handled when routing to the upstream. `auto` delegates host rewriting to Envoy, which rewrites the Host header using the upstream cluster host. `manual` disables automatic rewriting and expects explicit configuration.| + +oneOf + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|*anonymous*|object|false|none|none| + +xor + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|*anonymous*|object|false|none|none| + +#### Enumerated Values + +|Property|Value| +|---|---| +|hostRewrite|auto| +|hostRewrite|manual| + +

Operation

+ + + + + + +```json +{ + "method": "GET", + "path": "/books/{id}", + "policies": [ + { + "name": "cors", + "version": "v1", + "executionCondition": "request.metadata[authenticated] != true", + "params": {} + } + ] +} + +``` + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|method|string|true|none|HTTP method| +|path|string|true|none|Route path with optional {param} placeholders| +|policies|[[Policy](#schemapolicy)]|false|none|List of policies applied only to this operation (overrides or adds to API-level policies)| + +#### Enumerated Values + +|Property|Value| +|---|---| +|method|GET| +|method|POST| +|method|PUT| +|method|DELETE| +|method|PATCH| +|method|HEAD| +|method|OPTIONS| + +

Policy

+ + + + + + +```json +{ + "name": "cors", + "version": "v1", + "executionCondition": "request.metadata[authenticated] != true", + "params": {} +} + +``` + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|name|string|true|none|Name of the policy| +|version|string|true|none|Version of the policy. Only major-only version is allowed (e.g., v0, v1). Full semantic version (e.g., v1.0.0) is not accepted and will be rejected. The Gateway Controller resolves the major version to the single matching full version installed in the gateway image.| +|executionCondition|string|false|none|Expression controlling conditional execution of the policy| +|params|object|false|none|Arbitrary parameters for the policy (free-form key/value structure)| + +

WebhookAPIData

+ + + + + + +```json +{ + "displayName": "reading-list-api", + "version": "v1.0", + "context": "/weather", + "vhosts": { + "main": "api.example.com", + "sandbox": "sandbox-api.example.com" + }, + "allChannels": { + "on_subscription": { + "policies": [ + { + "name": "cors", + "version": "v1", + "executionCondition": "request.metadata[authenticated] != true", + "params": {} + } + ] + }, + "on_unsubscription": { + "policies": [ + { + "name": "cors", + "version": "v1", + "executionCondition": "request.metadata[authenticated] != true", + "params": {} + } + ] + }, + "on_message_received": { + "policies": [ + { + "name": "cors", + "version": "v1", + "executionCondition": "request.metadata[authenticated] != true", + "params": {} + } + ] + }, + "on_message_delivery": { + "policies": [ + { + "name": "cors", + "version": "v1", + "executionCondition": "request.metadata[authenticated] != true", + "params": {} + } + ] + } + }, + "channels": { + "property1": { + "on_subscription": { + "policies": [ + { + "name": "cors", + "version": "v1", + "executionCondition": "request.metadata[authenticated] != true", + "params": {} + } + ] + }, + "on_unsubscription": { + "policies": [ + { + "name": "cors", + "version": "v1", + "executionCondition": "request.metadata[authenticated] != true", + "params": {} + } + ] + }, + "on_message_received": { + "policies": [ + { + "name": "cors", + "version": "v1", + "executionCondition": "request.metadata[authenticated] != true", + "params": {} + } + ] + }, + "on_message_delivery": { + "policies": [ + { + "name": "cors", + "version": "v1", + "executionCondition": "request.metadata[authenticated] != true", + "params": {} + } + ] + } + }, + "property2": { + "on_subscription": { + "policies": [ + { + "name": "cors", + "version": "v1", + "executionCondition": "request.metadata[authenticated] != true", + "params": {} + } + ] + }, + "on_unsubscription": { + "policies": [ + { + "name": "cors", + "version": "v1", + "executionCondition": "request.metadata[authenticated] != true", + "params": {} + } + ] + }, + "on_message_received": { + "policies": [ + { + "name": "cors", + "version": "v1", + "executionCondition": "request.metadata[authenticated] != true", + "params": {} + } + ] + }, + "on_message_delivery": { + "policies": [ + { + "name": "cors", + "version": "v1", + "executionCondition": "request.metadata[authenticated] != true", + "params": {} + } + ] + } + } + }, + "deploymentState": "deployed" +} + +``` + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|displayName|string|true|none|Human-readable API name (must be URL-friendly - only letters, numbers, spaces, hyphens, underscores, and dots allowed)| +|version|string|true|none|Semantic version of the API| +|context|string|true|none|Base path for all API routes (must start with /, no trailing slash)| +|vhosts|object|false|none|Custom virtual hosts/domains for the API| +|main|string|true|none|Custom virtual host/domain for production traffic| +|sandbox|string|false|none|Custom virtual host/domain for sandbox traffic| +|allChannels|[WebSubAllChannelPolicies](#schemawebsuballchannelpolicies)|false|none|Policies applied to all channels, organized by event type.| +|channels|object|false|none|Per-channel configuration keyed by channel name. Each key is a channel name and defines policies applied only to that channel.| +|**additionalProperties**|[WebSubChannel](#schemawebsubchannel)|false|none|A single channel definition with optional per-channel policy overrides.| +|deploymentState|string|false|none|Desired deployment state - 'deployed' (default) or 'undeployed'. When set to 'undeployed', the API is removed from router traffic but configuration, API keys, and policies are preserved for potential redeployment.| + +#### Enumerated Values + +|Property|Value| +|---|---| +|deploymentState|deployed| +|deploymentState|undeployed| + +

WebSubChannel

+ + + + + + +```json +{ + "on_subscription": { + "policies": [ + { + "name": "cors", + "version": "v1", + "executionCondition": "request.metadata[authenticated] != true", + "params": {} + } + ] + }, + "on_unsubscription": { + "policies": [ + { + "name": "cors", + "version": "v1", + "executionCondition": "request.metadata[authenticated] != true", + "params": {} + } + ] + }, + "on_message_received": { + "policies": [ + { + "name": "cors", + "version": "v1", + "executionCondition": "request.metadata[authenticated] != true", + "params": {} + } + ] + }, + "on_message_delivery": { + "policies": [ + { + "name": "cors", + "version": "v1", + "executionCondition": "request.metadata[authenticated] != true", + "params": {} + } + ] + } +} + +``` + +A single channel definition with optional per-channel policy overrides. + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|on_subscription|[WebSubEventPolicies](#schemawebsubeventpolicies)|false|none|Policies for a single event type.| +|on_unsubscription|[WebSubEventPolicies](#schemawebsubeventpolicies)|false|none|Policies for a single event type.| +|on_message_received|[WebSubEventPolicies](#schemawebsubeventpolicies)|false|none|Policies for a single event type.| +|on_message_delivery|[WebSubEventPolicies](#schemawebsubeventpolicies)|false|none|Policies for a single event type.| + +

WebSubEventPolicies

+ + + + + + +```json +{ + "policies": [ + { + "name": "cors", + "version": "v1", + "executionCondition": "request.metadata[authenticated] != true", + "params": {} + } + ] +} + +``` + +Policies for a single event type. + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|policies|[[Policy](#schemapolicy)]|false|none|List of policies applied for this event type.| + +

WebSubAllChannelPolicies

+ + + + + + +```json +{ + "on_subscription": { + "policies": [ + { + "name": "cors", + "version": "v1", + "executionCondition": "request.metadata[authenticated] != true", + "params": {} + } + ] + }, + "on_unsubscription": { + "policies": [ + { + "name": "cors", + "version": "v1", + "executionCondition": "request.metadata[authenticated] != true", + "params": {} + } + ] + }, + "on_message_received": { + "policies": [ + { + "name": "cors", + "version": "v1", + "executionCondition": "request.metadata[authenticated] != true", + "params": {} + } + ] + }, + "on_message_delivery": { + "policies": [ + { + "name": "cors", + "version": "v1", + "executionCondition": "request.metadata[authenticated] != true", + "params": {} + } + ] + } +} + +``` + +Policies applied to all channels, organized by event type. + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|on_subscription|[WebSubEventPolicies](#schemawebsubeventpolicies)|false|none|Policies for a single event type.| +|on_unsubscription|[WebSubEventPolicies](#schemawebsubeventpolicies)|false|none|Policies for a single event type.| +|on_message_received|[WebSubEventPolicies](#schemawebsubeventpolicies)|false|none|Policies for a single event type.| +|on_message_delivery|[WebSubEventPolicies](#schemawebsubeventpolicies)|false|none|Policies for a single event type.| + +

WebSubChannelPolicies

+ + + + + + +```json +{ + "on_subscription": [ + { + "name": "cors", + "version": "v1", + "executionCondition": "request.metadata[authenticated] != true", + "params": {} + } + ], + "on_unsubscription": [ + { + "name": "cors", + "version": "v1", + "executionCondition": "request.metadata[authenticated] != true", + "params": {} + } + ], + "on_message_received": [ + { + "name": "cors", + "version": "v1", + "executionCondition": "request.metadata[authenticated] != true", + "params": {} + } + ], + "on_message_delivery": [ + { + "name": "cors", + "version": "v1", + "executionCondition": "request.metadata[authenticated] != true", + "params": {} + } + ] +} + +``` + +Policies applied to a specific channel, organized by event type. + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|on_subscription|[[Policy](#schemapolicy)]|false|none|Policies applied when a client subscribes to this channel (e.g., rbac)| +|on_unsubscription|[[Policy](#schemapolicy)]|false|none|Policies applied when a client unsubscribes from this channel| +|on_message_received|[[Policy](#schemapolicy)]|false|none|Policies applied when a message is received for this channel| +|on_message_delivery|[[Policy](#schemapolicy)]|false|none|Policies applied when delivering a message for this channel| + +

Channel

+ + + + + + +```json +{ + "name": "issues", + "method": "SUB", + "policies": [ + { + "name": "cors", + "version": "v1", + "executionCondition": "request.metadata[authenticated] != true", + "params": {} + } + ] +} + +``` + +Channel (topic/event stream) definition for async APIs. + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|name|string|true|none|Channel name or topic identifier relative to API context.| +|method|string|true|none|Operation method type.| +|policies|[[Policy](#schemapolicy)]|false|none|List of policies applied only to this channel (overrides or adds to API-level policies)| + +#### Enumerated Values + +|Property|Value| +|---|---| +|method|SUB| + +

WebBrokerApiRequest

+ + + + + + +```json +{ + "apiVersion": "gateway.api-platform.wso2.com/v1", + "kind": "WebBrokerApi", + "metadata": { + "name": "stock-trading-v1.0" + }, + "spec": { + "displayName": "Stock Trading WebBroker API", + "version": "v1.0", + "context": "/stock-trading/$version", + "receiver": { + "name": "websocket-receiver", + "type": "websocket" + }, + "broker": { + "name": "kafka-driver", + "type": "kafka", + "properties": { + "brokers": [ + "kafka-broker-1:9092", + "kafka-broker-2:9092" + ] + } + }, + "allChannels": { + "on_connection_init": { + "policies": [] + }, + "on_produce": { + "policies": [] + }, + "on_consume": { + "policies": [] + } + }, + "channels": { + "prices": { + "produceTo": { + "topic": "stock.prices" + }, + "consumeFrom": { + "topic": "stock.prices" + }, + "on_connection_init": { + "policies": [] + }, + "on_produce": { + "policies": [] + }, + "on_consume": { + "policies": [] + } + } + } + } +} + +``` + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|apiVersion|string|true|none|API specification version| +|kind|string|true|none|API type| +|metadata|[Metadata](#schemametadata)|true|none|none| +|spec|[WebBrokerApiData](#schemawebbrokerapidata)|true|none|none| + +#### Enumerated Values + +|Property|Value| +|---|---| +|apiVersion|gateway.api-platform.wso2.com/v1| +|kind|WebBrokerApi| + +

WebBrokerApi

+ + + + + + +```json +{ + "apiVersion": "gateway.api-platform.wso2.com/v1", + "kind": "WebBrokerApi", + "metadata": { + "name": "stock-trading-v1.0" + }, + "spec": { + "displayName": "Stock Trading WebBroker API", + "version": "v1.0", + "context": "/stock-trading/$version", + "receiver": { + "name": "websocket-receiver", + "type": "websocket" + }, + "broker": { + "name": "kafka-driver", + "type": "kafka", + "properties": { + "brokers": [ + "kafka-broker-1:9092", + "kafka-broker-2:9092" + ] + } + }, + "allChannels": { + "on_connection_init": { + "policies": [] + }, + "on_produce": { + "policies": [] + }, + "on_consume": { + "policies": [] + } + }, + "channels": { + "prices": { + "produceTo": { + "topic": "stock.prices" + }, + "consumeFrom": { + "topic": "stock.prices" + }, + "on_connection_init": { + "policies": [] + }, + "on_produce": { + "policies": [] + }, + "on_consume": { + "policies": [] + } + } + } + }, + "status": { + "id": "stock-trading-v1.0", + "state": "deployed", + "createdAt": "2026-04-24T07:21:13Z", + "updatedAt": "2026-04-24T07:21:13Z", + "deployedAt": "2026-04-24T07:21:13Z" + } +} + +``` + +### Properties + +allOf + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|*anonymous*|[WebBrokerApiRequest](#schemawebbrokerapirequest)|false|none|none| + +and + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|*anonymous*|object|false|none|none| +|status|[ResourceStatus](#schemaresourcestatus)|false|read-only|Server-managed lifecycle fields. Populated on responses.| + +

WebBrokerApiData

+ + + + + + +```json +{ + "displayName": "Stock Trading WebBroker API", + "version": "v1.0", + "context": "/stock-trading", + "receiver": { + "name": "websocket-receiver", + "type": "websocket", + "properties": {} + }, + "broker": { + "name": "kafka-driver", + "type": "kafka", + "properties": { + "brokers": [ + "kafka-broker-1:9092", + "kafka-broker-2:9092" + ] + } + }, + "allChannels": { + "on_connection_init": { + "policies": [ + { + "name": "cors", + "version": "v1", + "executionCondition": "request.metadata[authenticated] != true", + "params": {} + } + ] + }, + "on_produce": { + "policies": [ + { + "name": "cors", + "version": "v1", + "executionCondition": "request.metadata[authenticated] != true", + "params": {} + } + ] + }, + "on_consume": { + "policies": [ + { + "name": "cors", + "version": "v1", + "executionCondition": "request.metadata[authenticated] != true", + "params": {} + } + ] + } + }, + "channels": { + "property1": { + "produceTo": { + "topic": "stock.prices" + }, + "consumeFrom": { + "topic": "stock.prices" + }, + "on_connection_init": { + "policies": [ + { + "name": "cors", + "version": "v1", + "executionCondition": "request.metadata[authenticated] != true", + "params": {} + } + ] + }, + "on_produce": { + "policies": [ + { + "name": "cors", + "version": "v1", + "executionCondition": "request.metadata[authenticated] != true", + "params": {} + } + ] + }, + "on_consume": { + "policies": [ + { + "name": "cors", + "version": "v1", + "executionCondition": "request.metadata[authenticated] != true", + "params": {} + } + ] + } + }, + "property2": { + "produceTo": { + "topic": "stock.prices" + }, + "consumeFrom": { + "topic": "stock.prices" + }, + "on_connection_init": { + "policies": [ + { + "name": "cors", + "version": "v1", + "executionCondition": "request.metadata[authenticated] != true", + "params": {} + } + ] + }, + "on_produce": { + "policies": [ + { + "name": "cors", + "version": "v1", + "executionCondition": "request.metadata[authenticated] != true", + "params": {} + } + ] + }, + "on_consume": { + "policies": [ + { + "name": "cors", + "version": "v1", + "executionCondition": "request.metadata[authenticated] != true", + "params": {} + } + ] + } + } + }, + "vhosts": { + "main": "api.example.com", + "sandbox": "sandbox-api.example.com" + }, + "deploymentState": "deployed" +} + +``` + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|displayName|string|true|none|Human-readable API name (must be URL-friendly - only letters, numbers, spaces, hyphens, underscores, and dots allowed)| +|version|string|true|none|Semantic version of the API| +|context|string|true|none|Base path for all API routes (must start with /, no trailing slash)| +|receiver|[WebBrokerApiReceiver](#schemawebbrokerapireceiver)|true|none|WebSocket receiver configuration| +|broker|[WebBrokerApiBroker](#schemawebbrokerapibroker)|true|none|Message broker driver configuration| +|allChannels|[WebBrokerApiAllChannelPolicies](#schemawebbrokerapiallchannelpolicies)|false|none|Protocol mediation policies applied to all channels| +|channels|object|true|none|Map of WebSocket channels for bidirectional streaming with Kafka (key is channel name)| +|**additionalProperties**|[WebBrokerApiChannel](#schemawebbrokerapichannel)|false|none|WebSocket channel configuration with Kafka topic mapping| +|vhosts|object|false|none|Custom virtual hosts/domains for the API| +|main|string|true|none|Custom virtual host/domain for production traffic| +|sandbox|string|false|none|Custom virtual host/domain for sandbox traffic| +|deploymentState|string|false|none|Desired deployment state - 'deployed' (default) or 'undeployed'. When set to 'undeployed', the API is removed from router traffic but configuration and policies are preserved for potential redeployment.| + +#### Enumerated Values + +|Property|Value| +|---|---| +|deploymentState|deployed| +|deploymentState|undeployed| + +

WebBrokerApiReceiver

+ + + + + + +```json +{ + "name": "websocket-receiver", + "type": "websocket", + "properties": {} +} + +``` + +WebSocket receiver configuration + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|name|string|true|none|Receiver name| +|type|string|true|none|Receiver type| +|properties|object|false|none|Additional receiver properties| + +

WebBrokerApiBroker

+ + + + + + +```json +{ + "name": "kafka-driver", + "type": "kafka", + "properties": { + "brokers": [ + "kafka-broker-1:9092", + "kafka-broker-2:9092" + ] + } +} + +``` + +Message broker driver configuration + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|name|string|true|none|Broker driver name| +|type|string|true|none|Broker driver type| +|properties|object|true|none|Broker driver properties (e.g., bootstrap servers)| + +

WebBrokerApiAllChannelPolicies

+ + + + + + +```json +{ + "on_connection_init": { + "policies": [ + { + "name": "cors", + "version": "v1", + "executionCondition": "request.metadata[authenticated] != true", + "params": {} + } + ] + }, + "on_produce": { + "policies": [ + { + "name": "cors", + "version": "v1", + "executionCondition": "request.metadata[authenticated] != true", + "params": {} + } + ] + }, + "on_consume": { + "policies": [ + { + "name": "cors", + "version": "v1", + "executionCondition": "request.metadata[authenticated] != true", + "params": {} + } + ] + } +} + +``` + +Protocol mediation policies applied to all channels + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|on_connection_init|[WebBrokerApiPolicyGroup](#schemawebbrokerapipolicygroup)|false|none|Group of policies| +|on_produce|[WebBrokerApiPolicyGroup](#schemawebbrokerapipolicygroup)|false|none|Group of policies| +|on_consume|[WebBrokerApiPolicyGroup](#schemawebbrokerapipolicygroup)|false|none|Group of policies| + +

WebBrokerApiPolicyGroup

+ + + + + + +```json +{ + "policies": [ + { + "name": "cors", + "version": "v1", + "executionCondition": "request.metadata[authenticated] != true", + "params": {} + } + ] +} + +``` + +Group of policies + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|policies|[[Policy](#schemapolicy)]|false|none|List of policies to apply| + +

WebBrokerApiChannel

+ + + + + + +```json +{ + "produceTo": { + "topic": "stock.prices" + }, + "consumeFrom": { + "topic": "stock.prices" + }, + "on_connection_init": { + "policies": [ + { + "name": "cors", + "version": "v1", + "executionCondition": "request.metadata[authenticated] != true", + "params": {} + } + ] + }, + "on_produce": { + "policies": [ + { + "name": "cors", + "version": "v1", + "executionCondition": "request.metadata[authenticated] != true", + "params": {} + } + ] + }, + "on_consume": { + "policies": [ + { + "name": "cors", + "version": "v1", + "executionCondition": "request.metadata[authenticated] != true", + "params": {} + } + ] + } +} + +``` + +WebSocket channel configuration with Kafka topic mapping + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|produceTo|[WebBrokerApiProduceConfig](#schemawebbrokerapiproduceconfig)|false|none|Configuration for producing messages from WebSocket to Kafka| +|consumeFrom|[WebBrokerApiConsumeConfig](#schemawebbrokerapiconsumeconfig)|false|none|Configuration for consuming messages from Kafka to WebSocket| +|on_connection_init|[WebBrokerApiPolicyGroup](#schemawebbrokerapipolicygroup)|false|none|Group of policies| +|on_produce|[WebBrokerApiPolicyGroup](#schemawebbrokerapipolicygroup)|false|none|Group of policies| +|on_consume|[WebBrokerApiPolicyGroup](#schemawebbrokerapipolicygroup)|false|none|Group of policies| + +

WebBrokerApiProduceConfig

+ + + + + + +```json +{ + "topic": "stock.prices" +} + +``` + +Configuration for producing messages from WebSocket to Kafka + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|topic|string|true|none|Kafka topic to produce messages to| + +

WebBrokerApiConsumeConfig

+ + + + + + +```json +{ + "topic": "stock.prices" +} + +``` + +Configuration for consuming messages from Kafka to WebSocket + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|topic|string|true|none|Kafka topic to consume messages from| + +

APIKeyCreationRequest

+ + + + + + +```json +{ + "name": "my-production-key" +} + +``` + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|name|string|false|none|Identifier of the API key. If not provided, a default identifier will be generated| +|apiKey|string|false|none|Optional plain-text API key value for external key injection.
If provided, this key will be used instead of generating a new one.
The key will be hashed before storage. The key can be in any format
(minimum 36 characters). Use this for injecting externally generated
API keys.| +|maskedApiKey|string|false|none|Masked version of the API key for display purposes.
Provided by the platform API when injecting pre-hashed keys.| +|expiresIn|object|false|none|Expiration duration for the API key| +|unit|string|true|none|Time unit for expiration| +|duration|integer|true|none|Duration value for expiration| +|expiresAt|string(date-time)|false|none|Expiration timestamp. If both expiresIn and expiresAt are provided, expiresAt takes precedence.| +|externalRefId|string|false|none|External reference ID for the API key.
This field is optional and used for tracing purposes only.
The gateway generates its own internal ID for tracking.| +|issuer|string|false|none|Identifies the portal that created this key. If provided, only api keys generated from
the same portal will be accepted. If not provided, there is no portal restriction.| + +#### Enumerated Values + +|Property|Value| +|---|---| +|unit|seconds| +|unit|minutes| +|unit|hours| +|unit|days| +|unit|weeks| +|unit|months| + +

APIKeyCreationResponse

+ + + + + + +```json +{ + "status": "success", + "message": "API key generated successfully", + "remainingApiKeyQuota": 9, + "apiKey": { + "name": "my-production-key", + "displayName": "My Production Key", + "apiKey": "apip_1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef", + "apiId": "reading-list-api-v1.0", + "status": "active", + "createdAt": "2026-04-01T10:30:00Z", + "createdBy": "admin", + "expiresAt": null, + "source": "local" + } +} + +``` + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|status|string|true|none|none| +|message|string|true|none|none| +|remainingApiKeyQuota|integer|false|none|Remaining API key quota for the user| +|apiKey|[APIKey](#schemaapikey)|false|none|Details of an API key| + +

APIKey

+ + + + + + +```json +{ + "name": "my-production-key", + "displayName": "My Production Key", + "apiKey": "apip_1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef", + "apiId": "reading-list-api-v1.0", + "status": "active", + "createdAt": "2026-04-01T10:30:00Z", + "createdBy": "admin", + "expiresAt": null, + "source": "local" +} + +``` + +Details of an API key + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|name|string|true|none|URL-safe identifier for the API key (auto-generated from displayName, immutable, used as path parameter)| +|displayName|string|false|none|Human-readable name for the API key (user-provided, mutable)| +|apiKey|string|false|none|Generated API key with apip_ prefix| +|apiId|string|true|none|Unique public identifier of the API that the key is associated with| +|status|string|true|none|Status of the API key| +|createdAt|string(date-time)|true|none|Timestamp when the API key was generated| +|createdBy|string|true|none|Identifier of the user who generated the API key| +|expiresAt|string(date-time)¦null|true|none|Expiration timestamp (null if no expiration)| +|source|string|true|none|Source of the API key (local or external)| +|externalRefId|string|false|none|External reference ID for the API key| + +#### Enumerated Values + +|Property|Value| +|---|---| +|status|active| +|status|revoked| +|status|expired| +|source|local| +|source|external| + +

APIKeyRegenerationRequest

+ + + + + + +```json +{} + +``` + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|expiresIn|object|false|none|Expiration duration for the API key| +|unit|string|true|none|Time unit for expiration| +|duration|integer|true|none|Duration value for expiration| +|expiresAt|string(date-time)|false|none|Expiration timestamp| + +#### Enumerated Values + +|Property|Value| +|---|---| +|unit|seconds| +|unit|minutes| +|unit|hours| +|unit|days| +|unit|weeks| +|unit|months| + +

APIKeyUpdateRequest

+ + + + + + +```json +{ + "name": "my-production-key" +} + +``` + +### Properties + +*None* + +

APIKeyRevocationResponse

+ + + + + + +```json +{ + "status": "success", + "message": "API key revoked successfully" +} + +``` + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|status|string|true|none|none| +|message|string|true|none|none| + +

SubscriptionPlanCreateRequest

+ + + + + + +```json +{ + "planName": "Gold", + "billingPlan": "COMMERCIAL", + "stopOnQuotaReach": true, + "throttleLimitCount": 1000, + "throttleLimitUnit": "Hour", + "expiryTime": "2026-12-31T23:59:59Z", + "status": "ACTIVE" +} + +``` + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|planName|string|true|none|none| +|billingPlan|string|false|none|none| +|stopOnQuotaReach|boolean|false|none|none| +|throttleLimitCount|integer|false|none|none| +|throttleLimitUnit|string|false|none|none| +|expiryTime|string(date-time)|false|none|none| +|status|string|false|none|none| + +#### Enumerated Values + +|Property|Value| +|---|---| +|throttleLimitUnit|Min| +|throttleLimitUnit|Hour| +|throttleLimitUnit|Day| +|throttleLimitUnit|Month| +|status|ACTIVE| +|status|INACTIVE| + +

SubscriptionPlanUpdateRequest

+ + + + + + +```json +{ + "planName": "string", + "billingPlan": "string", + "stopOnQuotaReach": true, + "throttleLimitCount": 0, + "throttleLimitUnit": "Min", + "expiryTime": "2019-08-24T14:15:22Z", + "status": "ACTIVE" +} + +``` + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|planName|string|false|none|none| +|billingPlan|string|false|none|none| +|stopOnQuotaReach|boolean|false|none|none| +|throttleLimitCount|integer|false|none|none| +|throttleLimitUnit|string|false|none|none| +|expiryTime|string(date-time)|false|none|none| +|status|string|false|none|none| + +#### Enumerated Values + +|Property|Value| +|---|---| +|throttleLimitUnit|Min| +|throttleLimitUnit|Hour| +|throttleLimitUnit|Day| +|throttleLimitUnit|Month| +|status|ACTIVE| +|status|INACTIVE| + +

SubscriptionPlanResponse

+ + + + + + +```json +{ + "id": "string", + "planName": "string", + "billingPlan": "string", + "stopOnQuotaReach": true, + "throttleLimitCount": 0, + "throttleLimitUnit": "string", + "expiryTime": "2019-08-24T14:15:22Z", + "gatewayId": "string", + "status": "ACTIVE", + "createdAt": "2019-08-24T14:15:22Z", + "updatedAt": "2019-08-24T14:15:22Z" +} + +``` + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|id|string|false|none|none| +|planName|string|false|none|none| +|billingPlan|string|false|none|none| +|stopOnQuotaReach|boolean|false|none|none| +|throttleLimitCount|integer|false|none|none| +|throttleLimitUnit|string|false|none|none| +|expiryTime|string(date-time)|false|none|none| +|gatewayId|string|false|none|none| +|status|string|false|none|none| +|createdAt|string(date-time)|false|none|none| +|updatedAt|string(date-time)|false|none|none| + +#### Enumerated Values + +|Property|Value| +|---|---| +|status|ACTIVE| +|status|INACTIVE| + +

SubscriptionPlanListResponse

+ + + + + + +```json +{ + "subscriptionPlans": [ + { + "id": "string", + "planName": "string", + "billingPlan": "string", + "stopOnQuotaReach": true, + "throttleLimitCount": 0, + "throttleLimitUnit": "string", + "expiryTime": "2019-08-24T14:15:22Z", + "gatewayId": "string", + "status": "ACTIVE", + "createdAt": "2019-08-24T14:15:22Z", + "updatedAt": "2019-08-24T14:15:22Z" + } + ], + "count": 0 +} + +``` + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|subscriptionPlans|[[SubscriptionPlanResponse](#schemasubscriptionplanresponse)]|false|none|none| +|count|integer|false|none|none| + +

SubscriptionCreateRequest

+ + + + + + +```json +{ + "apiId": "c9f2b6ae-1234-5678-9abc-def012345678", + "subscriptionToken": "sub-token-abc123xyz", + "applicationId": "string", + "subscriptionPlanId": "string", + "billingCustomerId": "string", + "billingSubscriptionId": "string", + "status": "ACTIVE" +} + +``` + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|apiId|string|true|none|API identifier (deployment ID or handle)| +|subscriptionToken|string|true|none|Opaque subscription token for API invocation (required; stored as hash only)| +|applicationId|string|false|none|Application identifier (from DevPortal/STS). Optional for token-based subscriptions.| +|subscriptionPlanId|string|false|none|Subscription plan UUID for rate limit and billing configuration.| +|billingCustomerId|string|false|none|Billing customer identifier (optional, for analytics tracking).| +|billingSubscriptionId|string|false|none|Billing subscription identifier (optional, for analytics tracking).| +|status|string|false|none|none| + +#### Enumerated Values + +|Property|Value| +|---|---| +|status|ACTIVE| +|status|INACTIVE| +|status|REVOKED| + +

SubscriptionUpdateRequest

+ + + + + + +```json +{ + "status": "ACTIVE" +} + +``` + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|status|string|false|none|none| + +#### Enumerated Values + +|Property|Value| +|---|---| +|status|ACTIVE| +|status|INACTIVE| +|status|REVOKED| + +

SubscriptionResponse

+ + + + + + +```json +{ + "id": "string", + "apiId": "string", + "applicationId": "string", + "subscriptionToken": "string", + "subscriptionPlanId": "string", + "billingCustomerId": "string", + "billingSubscriptionId": "string", + "gatewayId": "string", + "status": "ACTIVE", + "createdAt": "2019-08-24T14:15:22Z", + "updatedAt": "2019-08-24T14:15:22Z" +} + +``` + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|id|string|false|none|none| +|apiId|string|false|none|none| +|applicationId|string|false|none|none| +|subscriptionToken|string|false|none|Opaque subscription token (returned only on create; use Platform-API to retrieve for existing subscriptions)| +|subscriptionPlanId|string|false|none|Subscription plan UUID| +|billingCustomerId|string|false|none|Billing customer identifier| +|billingSubscriptionId|string|false|none|Billing subscription identifier| +|gatewayId|string|false|none|none| +|status|string|false|none|none| +|createdAt|string(date-time)|false|none|none| +|updatedAt|string(date-time)|false|none|none| + +#### Enumerated Values + +|Property|Value| +|---|---| +|status|ACTIVE| +|status|INACTIVE| +|status|REVOKED| + +

SubscriptionListResponse

+ + + + + + +```json +{ + "subscriptions": [ + { + "id": "string", + "apiId": "string", + "applicationId": "string", + "subscriptionToken": "string", + "subscriptionPlanId": "string", + "billingCustomerId": "string", + "billingSubscriptionId": "string", + "gatewayId": "string", + "status": "ACTIVE", + "createdAt": "2019-08-24T14:15:22Z", + "updatedAt": "2019-08-24T14:15:22Z" + } + ], + "count": 0 +} + +``` + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|subscriptions|[[SubscriptionResponse](#schemasubscriptionresponse)]|false|none|none| +|count|integer|false|none|none| + +

MCPProxyConfigurationRequest

+ + + + + + +```json +{ + "apiVersion": "gateway.api-platform.wso2.com/v1", + "kind": "Mcp", + "metadata": { + "name": "everything-mcp-v1.0" + }, + "spec": { + "displayName": "Everything", + "version": "v1.0", + "context": "/everything", + "specVersion": "2025-06-18", + "upstream": { + "url": "http://everything:3001" + }, + "tools": [], + "resources": [], + "prompts": [] + } +} + +``` + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|apiVersion|string|true|none|MCP Proxy specification version| +|kind|string|true|none|MCP Proxy type| +|metadata|[Metadata](#schemametadata)|true|none|none| +|spec|[MCPProxyConfigData](#schemamcpproxyconfigdata)|true|none|none| + +#### Enumerated Values + +|Property|Value| +|---|---| +|apiVersion|gateway.api-platform.wso2.com/v1| +|kind|Mcp| + +

MCPProxyConfiguration

+ + + + + + +```json +{ + "apiVersion": "gateway.api-platform.wso2.com/v1", + "kind": "Mcp", + "metadata": { + "name": "everything-mcp-v1.0" + }, + "spec": { + "displayName": "Everything", + "version": "v1.0", + "context": "/everything", + "specVersion": "2025-06-18", + "upstream": { + "url": "http://everything:3001" + }, + "tools": [], + "resources": [], + "prompts": [] + }, + "status": { + "id": "everything-mcp-v1.0", + "state": "deployed", + "createdAt": "2026-04-24T07:21:13Z", + "updatedAt": "2026-04-24T07:21:13Z", + "deployedAt": "2026-04-24T07:21:13Z" + } +} + +``` + +### Properties + +allOf + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|*anonymous*|[MCPProxyConfigurationRequest](#schemamcpproxyconfigurationrequest)|false|none|none| + +and + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|*anonymous*|object|false|none|none| +|status|[ResourceStatus](#schemaresourcestatus)|false|read-only|Server-managed lifecycle fields. Populated on responses.| + +

MCPProxyConfigData

+ + + + + + +```json +{ + "displayName": "Everything", + "version": "v1.0", + "context": "/everything", + "specVersion": "2025-06-18", + "vhost": "mcp1.example.com", + "upstream": { + "url": "http://prod-backend:5000/api/v2", + "ref": "string", + "hostRewrite": "auto", + "auth": { + "type": "api-key", + "header": "string", + "value": "string" + } + }, + "policies": [ + { + "name": "cors", + "version": "v1", + "executionCondition": "request.metadata[authenticated] != true", + "params": {} + } + ], + "tools": [ + { + "name": "string", + "title": "string", + "description": "string", + "inputSchema": "string", + "outputSchema": "string" + } + ], + "resources": [ + { + "uri": "string", + "name": "string", + "title": "string", + "description": "string", + "mimeType": "string", + "size": 0 + } + ], + "prompts": [ + { + "name": "string", + "title": "string", + "description": "string", + "arguments": [ + { + "name": "string", + "description": "string", + "required": true, + "title": "string" + } + ] + } + ], + "deploymentState": "deployed" +} + +``` + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|displayName|string|true|none|Human-readable MCP Proxy display name| +|version|string|true|none|MCP Proxy version| +|context|string|false|none|MCP Proxy context path| +|specVersion|string|false|none|MCP specification version| +|vhost|string|false|none|Virtual host name used for routing. Supports standard domain names, subdomains, or wildcard domains. Must follow RFC-compliant hostname rules. Wildcards are only allowed in the left-most label (e.g., *.example.com).| +|upstream|any|true|none|The backend MCP server url and auth configurations| + +allOf + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|*anonymous*|[Upstream](#schemaupstream)|false|none|Upstream backend configuration (single target or reference)| + +and + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|*anonymous*|[UpstreamAuth](#schemaupstreamauth)|false|none|none| + +continued + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|policies|[[Policy](#schemapolicy)]|false|none|List of MCP Proxy level policies applied| +|tools|[[MCPTool](#schemamcptool)]|false|none|none| +|resources|[[MCPResource](#schemamcpresource)]|false|none|none| +|prompts|[[MCPPrompt](#schemamcpprompt)]|false|none|none| +|deploymentState|string|false|none|Desired deployment state - 'deployed' (default) or 'undeployed'. When set to 'undeployed', the MCP Proxy is removed from router traffic but configuration and policies are preserved for potential redeployment.| + +#### Enumerated Values + +|Property|Value| +|---|---| +|deploymentState|deployed| +|deploymentState|undeployed| + +

MCPTool

+ + + + + + +```json +{ + "name": "string", + "title": "string", + "description": "string", + "inputSchema": "string", + "outputSchema": "string" +} + +``` + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|name|string|true|none|Unique identifier for the tool| +|title|string|false|none|Optional human-readable name of the tool for display purposes.| +|description|string|true|none|Human-readable description of functionality| +|inputSchema|string|true|none|JSON Schema defining expected parameters| +|outputSchema|string|false|none|Optional JSON Schema defining expected output structure| + +

MCPResource

+ + + + + + +```json +{ + "uri": "string", + "name": "string", + "title": "string", + "description": "string", + "mimeType": "string", + "size": 0 +} + +``` + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|uri|string|true|none|Unique identifier for the resource| +|name|string|true|none|The name of the resource| +|title|string|false|none|Optional human-readable name of the resource for display purposes| +|description|string|false|none|Optional description| +|mimeType|string|false|none|Optional MIME type| +|size|integer|false|none|Optional size in bytes| + +

MCPPrompt

+ + + + + + +```json +{ + "name": "string", + "title": "string", + "description": "string", + "arguments": [ + { + "name": "string", + "description": "string", + "required": true, + "title": "string" + } + ] +} + +``` + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|name|string|true|none|Unique identifier for the prompt| +|title|string|false|none|Optional human-readable name of the prompt for display purposes| +|description|string|false|none|Optional human-readable description| +|arguments|[object]|false|none|Optional list of arguments for customization| +|name|string|true|none|Name of the argument| +|description|string|false|none|Description of the argument| +|required|boolean|false|none|Whether the argument is required| +|title|string|false|none|Optional human-readable title of the argument| + +

ErrorResponse

+ + + + + + +```json +{ + "status": "error", + "message": "Configuration validation failed", + "errors": [ + { + "field": "spec.context", + "message": "Context must start with / and cannot end with /" + } + ] +} + +``` + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|status|string|true|none|none| +|message|string|true|none|High-level error description| +|errors|[[ValidationError](#schemavalidationerror)]|false|none|Detailed validation errors| + +

ValidationError

+ + + + + + +```json +{ + "field": "spec.context", + "message": "Context must start with / and cannot end with /" +} + +``` + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|field|string|false|none|Field that failed validation| +|message|string|false|none|Human-readable error message| + +

LLMProviderTemplateRequest

+ + + + + + +```json +{ + "apiVersion": "gateway.api-platform.wso2.com/v1", + "kind": "LlmProviderTemplate", + "metadata": { + "name": "openai-template" + }, + "spec": { + "displayName": "OpenAI", + "promptTokens": { + "location": "payload", + "identifier": "$.usage.prompt_tokens" + }, + "completionTokens": { + "location": "payload", + "identifier": "$.usage.completion_tokens" + }, + "totalTokens": { + "location": "payload", + "identifier": "$.usage.total_tokens" + }, + "remainingTokens": { + "location": "header", + "identifier": "x-ratelimit-remaining-tokens" + }, + "requestModel": { + "location": "payload", + "identifier": "$.model" + }, + "responseModel": { + "location": "payload", + "identifier": "$.model" + } + } +} + +``` + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|apiVersion|string|true|none|Template specification version| +|kind|string|true|none|Template kind| +|metadata|[Metadata](#schemametadata)|true|none|none| +|spec|[LLMProviderTemplateData](#schemallmprovidertemplatedata)|true|none|none| + +#### Enumerated Values + +|Property|Value| +|---|---| +|apiVersion|gateway.api-platform.wso2.com/v1| +|kind|LlmProviderTemplate| + +

LLMProviderTemplate

+ + + + + + +```json +{ + "apiVersion": "gateway.api-platform.wso2.com/v1", + "kind": "LlmProviderTemplate", + "metadata": { + "name": "openai-template" + }, + "spec": { + "displayName": "OpenAI", + "promptTokens": { + "location": "payload", + "identifier": "$.usage.prompt_tokens" + }, + "completionTokens": { + "location": "payload", + "identifier": "$.usage.completion_tokens" + }, + "totalTokens": { + "location": "payload", + "identifier": "$.usage.total_tokens" + }, + "remainingTokens": { + "location": "header", + "identifier": "x-ratelimit-remaining-tokens" + }, + "requestModel": { + "location": "payload", + "identifier": "$.model" + }, + "responseModel": { + "location": "payload", + "identifier": "$.model" + } + }, + "status": { + "id": "openai-template", + "createdAt": "2026-04-24T07:21:13Z", + "updatedAt": "2026-04-24T07:21:13Z" + } +} + +``` + +### Properties + +allOf + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|*anonymous*|[LLMProviderTemplateRequest](#schemallmprovidertemplaterequest)|false|none|none| + +and + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|*anonymous*|object|false|none|none| +|status|[ResourceStatus](#schemaresourcestatus)|false|read-only|Server-managed lifecycle fields. Populated on responses.| + +

LLMProviderTemplateData

+ + + + + + +```json +{ + "displayName": "OpenAI", + "promptTokens": { + "location": "payload", + "identifier": "$.usage.inputTokens" + }, + "completionTokens": { + "location": "payload", + "identifier": "$.usage.inputTokens" + }, + "totalTokens": { + "location": "payload", + "identifier": "$.usage.inputTokens" + }, + "remainingTokens": { + "location": "payload", + "identifier": "$.usage.inputTokens" + }, + "requestModel": { + "location": "payload", + "identifier": "$.usage.inputTokens" + }, + "responseModel": { + "location": "payload", + "identifier": "$.usage.inputTokens" + }, + "resourceMappings": { + "resources": [ + { + "resource": "/responses", + "promptTokens": { + "location": "payload", + "identifier": "$.usage.inputTokens" + }, + "completionTokens": { + "location": "payload", + "identifier": "$.usage.inputTokens" + }, + "totalTokens": { + "location": "payload", + "identifier": "$.usage.inputTokens" + }, + "remainingTokens": { + "location": "payload", + "identifier": "$.usage.inputTokens" + }, + "requestModel": { + "location": "payload", + "identifier": "$.usage.inputTokens" + }, + "responseModel": { + "location": "payload", + "identifier": "$.usage.inputTokens" + } + } + ] + } +} + +``` + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|displayName|string|true|none|Human-readable LLM Template name| +|promptTokens|[ExtractionIdentifier](#schemaextractionidentifier)|false|none|none| +|completionTokens|[ExtractionIdentifier](#schemaextractionidentifier)|false|none|none| +|totalTokens|[ExtractionIdentifier](#schemaextractionidentifier)|false|none|none| +|remainingTokens|[ExtractionIdentifier](#schemaextractionidentifier)|false|none|none| +|requestModel|[ExtractionIdentifier](#schemaextractionidentifier)|false|none|none| +|responseModel|[ExtractionIdentifier](#schemaextractionidentifier)|false|none|none| +|resourceMappings|[LLMProviderTemplateResourceMappings](#schemallmprovidertemplateresourcemappings)|false|none|none| + +

LLMProviderTemplateResourceMappings

+ + + + + + +```json +{ + "resources": [ + { + "resource": "/responses", + "promptTokens": { + "location": "payload", + "identifier": "$.usage.inputTokens" + }, + "completionTokens": { + "location": "payload", + "identifier": "$.usage.inputTokens" + }, + "totalTokens": { + "location": "payload", + "identifier": "$.usage.inputTokens" + }, + "remainingTokens": { + "location": "payload", + "identifier": "$.usage.inputTokens" + }, + "requestModel": { + "location": "payload", + "identifier": "$.usage.inputTokens" + }, + "responseModel": { + "location": "payload", + "identifier": "$.usage.inputTokens" + } + } + ] +} + +``` + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|resources|[[LLMProviderTemplateResourceMapping](#schemallmprovidertemplateresourcemapping)]|false|none|none| + +

LLMProviderTemplateResourceMapping

+ + + + + + +```json +{ + "resource": "/responses", + "promptTokens": { + "location": "payload", + "identifier": "$.usage.inputTokens" + }, + "completionTokens": { + "location": "payload", + "identifier": "$.usage.inputTokens" + }, + "totalTokens": { + "location": "payload", + "identifier": "$.usage.inputTokens" + }, + "remainingTokens": { + "location": "payload", + "identifier": "$.usage.inputTokens" + }, + "requestModel": { + "location": "payload", + "identifier": "$.usage.inputTokens" + }, + "responseModel": { + "location": "payload", + "identifier": "$.usage.inputTokens" + } +} + +``` + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|resource|string|true|none|Resource path pattern for this mapping| +|promptTokens|[ExtractionIdentifier](#schemaextractionidentifier)|false|none|none| +|completionTokens|[ExtractionIdentifier](#schemaextractionidentifier)|false|none|none| +|totalTokens|[ExtractionIdentifier](#schemaextractionidentifier)|false|none|none| +|remainingTokens|[ExtractionIdentifier](#schemaextractionidentifier)|false|none|none| +|requestModel|[ExtractionIdentifier](#schemaextractionidentifier)|false|none|none| +|responseModel|[ExtractionIdentifier](#schemaextractionidentifier)|false|none|none| + +

ExtractionIdentifier

+ + + + + + +```json +{ + "location": "payload", + "identifier": "$.usage.inputTokens" +} + +``` + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|location|string|true|none|Where to find the token information| +|identifier|string|true|none|JSONPath expression or header name to identify the token value| + +#### Enumerated Values + +|Property|Value| +|---|---| +|location|payload| +|location|header| +|location|queryParam| +|location|pathParam| + +

LLMProviderConfigurationRequest

+ + + + + + +```json +{ + "apiVersion": "gateway.api-platform.wso2.com/v1", + "kind": "LlmProvider", + "metadata": { + "name": "wso2-openai-provider" + }, + "spec": { + "displayName": "OpenAI Provider", + "version": "v1.0", + "template": "openai", + "context": "/openai/latest", + "upstream": { + "url": "https://api.openai.com/v1", + "auth": { + "type": "api-key", + "header": "Authorization", + "value": "Bearer sk-your-api-key" + } + }, + "accessControl": { + "mode": "deny_all", + "exceptions": [ + { + "path": "/chat/completions", + "methods": [ + "POST" + ] + }, + { + "path": "/models", + "methods": [ + "GET" + ] + }, + { + "path": "/models/{modelId}", + "methods": [ + "GET" + ] + } + ] + } + } +} + +``` + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|apiVersion|string|true|none|Provider specification version| +|kind|string|true|none|Provider kind| +|metadata|[Metadata](#schemametadata)|true|none|none| +|spec|[LLMProviderConfigData](#schemallmproviderconfigdata)|true|none|none| + +#### Enumerated Values + +|Property|Value| +|---|---| +|apiVersion|gateway.api-platform.wso2.com/v1| +|kind|LlmProvider| + +

LLMProviderConfiguration

+ + + + + + +```json +{ + "apiVersion": "gateway.api-platform.wso2.com/v1", + "kind": "LlmProvider", + "metadata": { + "name": "wso2-openai-provider" + }, + "spec": { + "displayName": "OpenAI Provider", + "version": "v1.0", + "template": "openai", + "context": "/openai/latest", + "upstream": { + "url": "https://api.openai.com/v1", + "auth": { + "type": "api-key", + "header": "Authorization", + "value": "Bearer sk-your-api-key" + } + }, + "accessControl": { + "mode": "deny_all", + "exceptions": [ + { + "path": "/chat/completions", + "methods": [ + "POST" + ] + }, + { + "path": "/models", + "methods": [ + "GET" + ] + }, + { + "path": "/models/{modelId}", + "methods": [ + "GET" + ] + } + ] + } + }, + "status": { + "id": "wso2-openai-provider", + "state": "deployed", + "createdAt": "2026-04-24T07:21:13Z", + "updatedAt": "2026-04-24T07:21:13Z", + "deployedAt": "2026-04-24T07:21:13Z" + } +} + +``` + +### Properties + +allOf + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|*anonymous*|[LLMProviderConfigurationRequest](#schemallmproviderconfigurationrequest)|false|none|none| + +and + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|*anonymous*|object|false|none|none| +|status|[ResourceStatus](#schemaresourcestatus)|false|read-only|Server-managed lifecycle fields. Populated on responses.| + +

LLMProviderConfigData

+ + + + + + +```json +{ + "displayName": "WSO2 OpenAI Provider", + "version": "v1.0", + "context": "/openai", + "vhost": "api.openai.com", + "template": "openai", + "upstream": { + "url": "http://prod-backend:5000/api/v2", + "ref": "string", + "hostRewrite": "auto", + "auth": { + "type": "api-key", + "header": "string", + "value": "string" + } + }, + "accessControl": { + "mode": "deny_all", + "exceptions": [ + { + "path": "/chat/completions", + "methods": [ + "GET" + ] + } + ] + }, + "policies": [ + { + "name": "llm-cost-based-ratelimit", + "version": "v1", + "paths": [ + { + "path": "/chat/completions", + "methods": [ + "GET" + ], + "params": {} + } + ] + } + ], + "deploymentState": "deployed" +} + +``` + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|displayName|string|true|none|Human-readable LLM Provider name| +|version|string|true|none|Semantic version of the LLM Provider| +|context|string|false|none|Base path for all API routes (must start with /, no trailing slash)| +|vhost|string|false|none|Virtual host name used for routing. Supports standard domain names, subdomains, or wildcard domains. Must follow RFC-compliant hostname rules. Wildcards are only allowed in the left-most label (e.g., *.example.com).| +|template|string|true|none|Template name to use for this LLM Provider| +|upstream|any|true|none|none| + +allOf + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|*anonymous*|[Upstream](#schemaupstream)|false|none|Upstream backend configuration (single target or reference)| + +and + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|*anonymous*|[UpstreamAuth](#schemaupstreamauth)|false|none|none| + +continued + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|accessControl|[LLMAccessControl](#schemallmaccesscontrol)|true|none|none| +|policies|[[LLMPolicy](#schemallmpolicy)]|false|none|List of policies applied only to this operation (overrides or adds to API-level policies)| +|deploymentState|string|false|none|Desired deployment state - 'deployed' (default) or 'undeployed'. When set to 'undeployed', the LLM Provider is removed from router traffic but configuration and policies are preserved for potential redeployment.| + +#### Enumerated Values + +|Property|Value| +|---|---| +|deploymentState|deployed| +|deploymentState|undeployed| + +

UpstreamAuth

+ + + + + + +```json +{ + "auth": { + "type": "api-key", + "header": "string", + "value": "string" + } +} + +``` + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|auth|object|false|none|none| +|type|string|true|none|none| +|header|string|false|none|none| +|value|string|false|none|none| + +#### Enumerated Values + +|Property|Value| +|---|---| +|type|api-key| + +

LLMUpstreamAuth

+ + + + + + +```json +{ + "type": "api-key", + "header": "string", + "value": "string" +} + +``` + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|type|string|true|none|none| +|header|string|false|none|none| +|value|string|false|none|none| + +#### Enumerated Values + +|Property|Value| +|---|---| +|type|api-key| + +

LLMProxyProvider

+ + + + + + +```json +{ + "id": "wso2-openai-provider", + "auth": { + "type": "api-key", + "header": "string", + "value": "string" + } +} + +``` + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|id|string|true|none|Unique id of a deployed llm provider| +|auth|[LLMUpstreamAuth](#schemallmupstreamauth)|false|none|none| + +

LLMAccessControl

+ + + + + + +```json +{ + "mode": "deny_all", + "exceptions": [ + { + "path": "/chat/completions", + "methods": [ + "GET" + ] + } + ] +} + +``` + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|mode|string|true|none|Access control mode| +|exceptions|[[RouteException](#schemarouteexception)]|false|none|Path exceptions to the access control mode| + +#### Enumerated Values + +|Property|Value| +|---|---| +|mode|allow_all| +|mode|deny_all| + +

RouteException

+ + + + + + +```json +{ + "path": "/chat/completions", + "methods": [ + "GET" + ] +} + +``` + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|path|string|true|none|Path pattern| +|methods|[string]|true|none|HTTP methods| + +

LLMPolicy

+ + + + + + +```json +{ + "name": "llm-cost-based-ratelimit", + "version": "v1", + "paths": [ + { + "path": "/chat/completions", + "methods": [ + "GET" + ], + "params": {} + } + ] +} + +``` + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|name|string|true|none|none| +|version|string|true|none|none| +|paths|[[LLMPolicyPath](#schemallmpolicypath)]|true|none|none| + +

LLMPolicyPath

+ + + + + + +```json +{ + "path": "/chat/completions", + "methods": [ + "GET" + ], + "params": {} +} + +``` + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|path|string|true|none|none| +|methods|[string]|true|none|none| +|params|object|true|none|JSON Schema describing the parameters accepted by this policy. This itself is a JSON Schema document.| + +

LLMProxyConfigurationRequest

+ + + + + + +```json +{ + "apiVersion": "gateway.api-platform.wso2.com/v1", + "kind": "LlmProxy", + "metadata": { + "name": "openai-proxy" + }, + "spec": { + "displayName": "OpenAI Proxy", + "version": "v1.0", + "context": "/openai-proxy", + "provider": { + "id": "wso2-openai-provider" + }, + "policies": [] + } +} + +``` + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|apiVersion|string|true|none|Proxy specification version| +|kind|string|true|none|Proxy kind| +|metadata|[Metadata](#schemametadata)|true|none|none| +|spec|[LLMProxyConfigData](#schemallmproxyconfigdata)|true|none|none| + +#### Enumerated Values + +|Property|Value| +|---|---| +|apiVersion|gateway.api-platform.wso2.com/v1| +|kind|LlmProxy| + +

LLMProxyConfiguration

+ + + + + + +```json +{ + "apiVersion": "gateway.api-platform.wso2.com/v1", + "kind": "LlmProxy", + "metadata": { + "name": "openai-proxy" + }, + "spec": { + "displayName": "OpenAI Proxy", + "version": "v1.0", + "context": "/openai-proxy", + "provider": { + "id": "wso2-openai-provider" + }, + "policies": [] + }, + "status": { + "id": "openai-proxy", + "state": "deployed", + "createdAt": "2026-04-24T07:21:13Z", + "updatedAt": "2026-04-24T07:21:13Z", + "deployedAt": "2026-04-24T07:21:13Z" + } +} + +``` + +### Properties + +allOf + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|*anonymous*|[LLMProxyConfigurationRequest](#schemallmproxyconfigurationrequest)|false|none|none| + +and + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|*anonymous*|object|false|none|none| +|status|[ResourceStatus](#schemaresourcestatus)|false|read-only|Server-managed lifecycle fields. Populated on responses.| + +

LLMProxyConfigData

+ + + + + + +```json +{ + "displayName": "OpenAI Proxy", + "version": "v1.0", + "context": "/openai", + "vhost": "api.openai.com", + "provider": { + "id": "wso2-openai-provider", + "auth": { + "type": "api-key", + "header": "string", + "value": "string" + } + }, + "policies": [ + { + "name": "llm-cost-based-ratelimit", + "version": "v1", + "paths": [ + { + "path": "/chat/completions", + "methods": [ + "GET" + ], + "params": {} + } + ] + } + ], + "deploymentState": "deployed" +} + +``` + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|displayName|string|true|none|Human-readable LLM proxy name (must be URL-friendly - only letters, numbers, spaces, hyphens, underscores, and dots allowed)| +|version|string|true|none|Semantic version of the LLM proxy| +|context|string|false|none|Base path for all API routes (must start with /, no trailing slash)| +|vhost|string|false|none|Virtual host name used for routing. Supports standard domain names, subdomains, or wildcard domains. Must follow RFC-compliant hostname rules. Wildcards are only allowed in the left-most label (e.g., *.example.com).| +|provider|[LLMProxyProvider](#schemallmproxyprovider)|true|none|none| +|policies|[[LLMPolicy](#schemallmpolicy)]|false|none|List of policies applied only to this operation (overrides or adds to API-level policies)| +|deploymentState|string|false|none|Desired deployment state - 'deployed' (default) or 'undeployed'. When set to 'undeployed', the LLM Proxy is removed from router traffic but configuration and policies are preserved for potential redeployment.| + +#### Enumerated Values + +|Property|Value| +|---|---| +|deploymentState|deployed| +|deploymentState|undeployed| + +

SecretConfigurationRequest

+ + + + + + +```json +{ + "apiVersion": "gateway.api-platform.wso2.com/v1", + "kind": "Secret", + "metadata": { + "name": "database-password" + }, + "spec": { + "displayName": "Database Password", + "description": "PostgreSQL main database password", + "value": "sup3rs3cr3t!" + } +} + +``` + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|apiVersion|string|true|none|Secret specification version| +|kind|string|true|none|Secret resource kind| +|metadata|[Metadata](#schemametadata)|true|none|none| +|spec|[SecretConfigData](#schemasecretconfigdata)|true|none|none| + +#### Enumerated Values + +|Property|Value| +|---|---| +|apiVersion|gateway.api-platform.wso2.com/v1| +|kind|Secret| + +

SecretConfiguration

+ + + + + + +```json +{ + "apiVersion": "gateway.api-platform.wso2.com/v1", + "kind": "Secret", + "metadata": { + "name": "database-password" + }, + "spec": { + "displayName": "Database Password", + "description": "PostgreSQL main database password", + "value": "sup3rs3cr3t!" + }, + "status": { + "id": "database-password", + "createdAt": "2026-04-24T07:21:13Z", + "updatedAt": "2026-04-24T07:21:13Z" + } +} + +``` + +Composite of request fields plus a generic k8s-style `status` for documentation. +For actual HTTP response shapes, use `SecretConfigurationResponseCreateUpdate`, +`SecretConfigurationResponseRetrieved`, or `SecretListItem` (see +`SecretResourceServiceStatus` for the id/timestamp-only status on secret APIs). + +### Properties + +allOf + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|*anonymous*|[SecretConfigurationRequest](#schemasecretconfigurationrequest)|false|none|none| + +and + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|*anonymous*|object|false|none|none| +|status|[ResourceStatus](#schemaresourcestatus)|false|read-only|Server-managed lifecycle fields. Populated on responses.| + +

SecretConfigData

+ + + + + + +```json +{ + "displayName": "Database Password", + "description": "PostgreSQL main database password", + "value": "sup3rs3cr3t!" +} + +``` + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|displayName|string|true|none|Human-readable secret name (must be URL-friendly - only letters, numbers, spaces, hyphens, underscores, and dots allowed)| +|description|string|false|none|Description of the secret| +|value|string(password)|true|none|Secret value (stored encrypted)| + +

SecretConfigListData

+ + + + + + +```json +{ + "displayName": "Database Password", + "description": "PostgreSQL main database password" +} + +``` + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|displayName|string|true|none|Human-readable secret name (must be URL-friendly - only letters, numbers, spaces, hyphens, underscores, and dots allowed)| +|description|string|false|none|Description of the secret, if the server includes it| + +

SecretListItem

+ + + + + + +```json +{ + "apiVersion": "gateway.api-platform.wso2.com/v1", + "kind": "Secret", + "metadata": { + "name": "database-password" + }, + "spec": { + "displayName": "Database Password" + }, + "status": { + "id": "database-password", + "createdAt": "2026-04-24T07:21:13Z", + "updatedAt": "2026-04-24T07:21:13Z" + } +} + +``` + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|apiVersion|string|true|none|Secret specification version| +|kind|string|true|none|Secret resource kind| +|metadata|[Metadata](#schemametadata)|true|none|none| +|spec|[SecretConfigListData](#schemasecretconfiglistdata)|true|none|none| +|status|[ResourceStatus](#schemaresourcestatus)|false|read-only|Server-managed lifecycle fields. Omitted in list items may vary; the secret value is never included here.| + +#### Enumerated Values + +|Property|Value| +|---|---| +|apiVersion|gateway.api-platform.wso2.com/v1| +|kind|Secret| + +

CertificateUploadRequest

+ + + + + + +```json +{ + "name": "my-custom-ca", + "certificate": "-----BEGIN CERTIFICATE-----\nMIIDXTCCAkWgAwIBAgIJAKL0UG+mRKtjMA0GCSqGSIb3DQEBCwUAMEUxCzAJBgNV\n...\n-----END CERTIFICATE-----\n" +} + +``` + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|name|string|true|none|Unique name for the certificate. Must be unique across all certificates.| +|certificate|string|true|none|PEM-encoded X.509 certificate(s). Can contain multiple certificates.| + +

CertificateResponse

+ + + + + + +```json +{ + "id": "550e8400-e29b-41d4-a716-446655440000", + "name": "my-custom-ca", + "subject": "CN=My CA,O=My Organization,C=US", + "issuer": "CN=My CA,O=My Organization,C=US", + "notAfter": "2026-11-26 06:07:26", + "count": 1, + "message": "Certificate uploaded and SDS updated successfully", + "status": "success" +} + +``` + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|id|string|false|none|Unique identifier (UUID) for the certificate| +|name|string|false|none|Name of the certificate| +|subject|string|false|none|Certificate subject DN (for first cert if bundle)| +|issuer|string|false|none|Certificate issuer DN (for first cert if bundle)| +|notAfter|string(date-time)|false|none|Certificate expiration date (for first cert if bundle)| +|count|integer|false|none|Number of certificates in the file| +|message|string|false|none|Success or informational message| +|status|string|false|none|none| + +#### Enumerated Values + +|Property|Value| +|---|---| +|status|success| +|status|error| + +

CertificateListResponse

+ + + + + + +```json +{ + "certificates": [ + { + "id": "550e8400-e29b-41d4-a716-446655440000", + "name": "my-custom-ca", + "subject": "CN=My CA,O=My Organization,C=US", + "issuer": "CN=My CA,O=My Organization,C=US", + "notAfter": "2026-11-26 06:07:26", + "count": 1, + "message": "Certificate uploaded and SDS updated successfully", + "status": "success" + } + ], + "totalCount": 3, + "totalBytes": 221599, + "status": "success" +} + +``` + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|certificates|[[CertificateResponse](#schemacertificateresponse)]|false|none|none| +|totalCount|integer|false|none|Total number of certificate files| +|totalBytes|integer|false|none|Total bytes of all certificate files| +|status|string|false|none|none| + +

APIKeyListResponse

+ + + + + + +```json +{ + "apiKeys": [ + { + "name": "my-production-key", + "displayName": "My Production Key", + "apiKey": "apip_1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef", + "apiId": "reading-list-api-v1.0", + "status": "active", + "createdAt": "2026-04-01T10:30:00Z", + "createdBy": "admin", + "expiresAt": null, + "source": "local" + } + ], + "totalCount": 3, + "status": "success" +} + +``` + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|apiKeys|[[APIKey](#schemaapikey)]|false|none|[Details of an API key]| +|totalCount|integer|false|none|Total number of API keys| +|status|string|false|none|none| + +

SecretListResponse

+ + + + + + +```json +{ + "status": "success", + "count": 5, + "secrets": [ + { + "apiVersion": "gateway.api-platform.wso2.com/v1", + "kind": "Secret", + "metadata": { + "name": "database-password" + }, + "spec": { + "displayName": "Database Password" + }, + "status": { + "id": "database-password", + "createdAt": "2026-04-24T07:21:13Z", + "updatedAt": "2026-04-24T07:21:13Z" + } + } + ] +} + +``` + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|status|string|false|none|none| +|count|integer|false|none|Total number of secrets| +|secrets|[[SecretListItem](#schemasecretlistitem)]|false|none|List of secrets. For security, the spec.value field is omitted for every item in the list; retrieve a single secret by id to obtain the decrypted value.| + +

SecretResourceServiceStatus

+ + + + + + +```json +{ + "id": "database-password", + "createdAt": "2026-01-05T10:30:00Z", + "updatedAt": "2026-01-05T10:30:00Z" +} + +``` + +Id and optional timestamps. Not the full ResourceStatus model (no `state` or +`deployedAt`). + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|id|string|true|none|Same as metadata.name / secret handle| +|createdAt|string(date-time)|false|none|none| +|updatedAt|string(date-time)|false|none|none| + +

SecretConfigurationResponseCreateUpdate

+ + + + + + +```json +{ + "apiVersion": "gateway.api-platform.wso2.com/v1", + "kind": "Secret", + "metadata": { + "name": "database-password" + }, + "spec": { + "displayName": "Database Password", + "description": "PostgreSQL main database password" + }, + "status": { + "id": "database-password", + "createdAt": "2026-01-05T10:30:00Z", + "updatedAt": "2026-01-05T10:30:00Z" + } +} + +``` + +POST/PUT /secrets response. `spec.value` is not returned; see SecretConfigurationRequest for create/update request bodies. + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|apiVersion|string|true|none|none| +|kind|string|true|none|none| +|metadata|[Metadata](#schemametadata)|true|none|none| +|spec|[SecretConfigListData](#schemasecretconfiglistdata)|true|none|none| +|status|[SecretResourceServiceStatus](#schemasecretresourceservicestatus)|true|none|Id and optional timestamps. Not the full ResourceStatus model (no `state` or
`deployedAt`).| + +#### Enumerated Values + +|Property|Value| +|---|---| +|apiVersion|gateway.api-platform.wso2.com/v1| +|kind|Secret| + +

SecretConfigurationResponseRetrieved

+ + + + + + +```json +{ + "apiVersion": "gateway.api-platform.wso2.com/v1", + "kind": "Secret", + "metadata": { + "name": "database-password" + }, + "spec": { + "displayName": "Database Password", + "description": "PostgreSQL main database password", + "value": "sup3rs3cr3t!" + }, + "status": { + "id": "database-password", + "createdAt": "2026-01-05T10:30:00Z", + "updatedAt": "2026-01-05T10:30:00Z" + } +} + +``` + +GET /secrets/{id} response including decrypted `spec.value`. + +### Properties + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|apiVersion|string|true|none|none| +|kind|string|true|none|none| +|metadata|[Metadata](#schemametadata)|true|none|none| +|spec|[SecretConfigData](#schemasecretconfigdata)|true|none|none| +|status|[SecretResourceServiceStatus](#schemasecretresourceservicestatus)|true|none|Id and optional timestamps. Not the full ResourceStatus model (no `state` or
`deployedAt`).| + +#### Enumerated Values + +|Property|Value| +|---|---| +|apiVersion|gateway.api-platform.wso2.com/v1| +|kind|Secret| \ No newline at end of file diff --git a/en/docs/api-gateway/1.2.0/gateway-controller-management-api/secrets-management.md b/en/docs/api-gateway/1.2.0/gateway-controller-management-api/secrets-management.md new file mode 100644 index 000000000..660d79f47 --- /dev/null +++ b/en/docs/api-gateway/1.2.0/gateway-controller-management-api/secrets-management.md @@ -0,0 +1,438 @@ +--- +title: "Management API: Secrets Management" +description: "REST API reference for creating, listing, retrieving, updating, and deleting secrets in API Platform Gateway." +canonical_url: https://wso2.com/api-platform/docs/api-gateway/gateway-controller-management-api/secrets-management/ +md_url: https://wso2.com/api-platform/docs/api-gateway/gateway-controller-management-api/secrets-management.md +tags: + - api-gateway + - management-api + - security +author: WSO2 API Platform Documentation Team +last_updated: 2026-06-17 +content_type: "reference" +--- + +# Secrets Management + +CRUD operations for Secrets + +## List all secrets + + + +`GET /secrets` + +> Code samples + +```shell + +curl -X GET http://localhost:9090/api/management/v1/secrets \ + -u {username}:{password} \ + -H 'Accept: application/json' + +``` + +Retrieve a list of all stored secrets. Returns secret identifiers without +the actual secret values for security purposes. + +### Authentication + + + +> Example responses + +> 200 Response + +```json +{ + "status": "success", + "count": 5, + "secrets": [ + { + "apiVersion": "gateway.api-platform.wso2.com/v1", + "kind": "Secret", + "metadata": { + "name": "database-password" + }, + "spec": { + "displayName": "Database Password" + }, + "status": { + "id": "database-password", + "createdAt": "2026-04-24T07:21:13Z", + "updatedAt": "2026-04-24T07:21:13Z" + } + } + ] +} +``` + +

Responses

+ +|Status|Meaning|Description|Schema| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|List of secrets retrieved successfully|[SecretListResponse](schemas.md#schemasecretlistresponse)| +|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|Unauthorized - authentication required|[ErrorResponse](schemas.md#schemaerrorresponse)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal server error|[ErrorResponse](schemas.md#schemaerrorresponse)| + +## Create a new secret + + + +`POST /secrets` + +> Code samples + +```shell + +curl -X POST http://localhost:9090/api/management/v1/secrets \ + -u {username}:{password} \ + -H 'Content-Type: application/json' \ + -H 'Accept: application/json' \ + -d @payload.json + +``` + +Stores a new secret encrypted at rest. The secret ID must be unique. +The value is encrypted using the primary encryption provider before persistence. + +> Payload + +```json +{ + "apiVersion": "gateway.api-platform.wso2.com/v1", + "kind": "Secret", + "metadata": { + "name": "database-password" + }, + "spec": { + "displayName": "Database Password", + "description": "PostgreSQL main database password", + "value": "sup3rs3cr3t!" + } +} +``` + +### Authentication + + + +

Parameters

+ +|Name|In|Type|Required|Description| +|---|---|---|---|---| +|body|body|[SecretConfigurationRequest](schemas.md#schemasecretconfigurationrequest)|true|none| + +> Example responses + +> Secret created successfully + +```json +{ + "apiVersion": "gateway.api-platform.wso2.com/v1", + "kind": "Secret", + "metadata": { + "name": "database-password" + }, + "spec": { + "displayName": "Database Password", + "description": "PostgreSQL main database password" + }, + "status": { + "id": "database-password", + "createdAt": "2026-01-05T10:30:00Z", + "updatedAt": "2026-01-05T10:30:00Z" + } +} +``` + +> 400 Response + +```json +{ + "status": "error", + "message": "Configuration validation failed", + "errors": [ + { + "field": "spec.context", + "message": "Context must start with / and cannot end with /" + } + ] +} +``` + +

Responses

+ +|Status|Meaning|Description|Schema| +|---|---|---|---| +|201|[Created](https://tools.ietf.org/html/rfc7231#section-6.3.2)|Secret created successfully|[SecretConfigurationResponseCreateUpdate](schemas.md#schemasecretconfigurationresponsecreateupdate)| +|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad request - missing or invalid fields|[ErrorResponse](schemas.md#schemaerrorresponse)| +|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|Unauthorized - authentication required|[ErrorResponse](schemas.md#schemaerrorresponse)| +|409|[Conflict](https://tools.ietf.org/html/rfc7231#section-6.5.8)|Conflict - secret with this ID already exists|[ErrorResponse](schemas.md#schemaerrorresponse)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal server error - encryption failed|[ErrorResponse](schemas.md#schemaerrorresponse)| + +## Retrieve a secret + + + +`GET /secrets/{id}` + +> Code samples + +```shell + +curl -X GET http://localhost:9090/api/management/v1/secrets/{id} \ + -u {username}:{password} \ + -H 'Accept: application/json' + +``` + +Retrieves and decrypts a secret. The secret value is decrypted using the +encryption provider chain before being returned. If all providers fail to +decrypt the secret, a 500 error is returned with a generic message. + +### Authentication + + + +

Parameters

+ +|Name|In|Type|Required|Description| +|---|---|---|---|---| +|id|path|string|true|Unique secret identifier| + +> Example responses + +> Secret retrieved and decrypted successfully + +```json +{ + "apiVersion": "gateway.api-platform.wso2.com/v1", + "kind": "Secret", + "metadata": { + "name": "database-password" + }, + "spec": { + "displayName": "Database Password", + "description": "PostgreSQL main database password", + "value": "sup3rs3cr3t!" + }, + "status": { + "id": "database-password", + "createdAt": "2026-01-05T10:30:00Z", + "updatedAt": "2026-01-05T10:30:00Z" + } +} +``` + +> 401 Response + +```json +{ + "status": "error", + "message": "Configuration validation failed", + "errors": [ + { + "field": "spec.context", + "message": "Context must start with / and cannot end with /" + } + ] +} +``` + +

Responses

+ +|Status|Meaning|Description|Schema| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|Secret retrieved and decrypted successfully|[SecretConfigurationResponseRetrieved](schemas.md#schemasecretconfigurationresponseretrieved)| +|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|Unauthorized - authentication required|[ErrorResponse](schemas.md#schemaerrorresponse)| +|404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Secret configuration not found|[ErrorResponse](schemas.md#schemaerrorresponse)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal server error - decryption failed|[ErrorResponse](schemas.md#schemaerrorresponse)| + +## Update a secret + + + +`PUT /secrets/{id}` + +> Code samples + +```shell + +curl -X PUT http://localhost:9090/api/management/v1/secrets/{id} \ + -u {username}:{password} \ + -H 'Content-Type: application/json' \ + -H 'Accept: application/json' \ + -d @payload.json + +``` + +Updates an existing secret with a new value. The new value is encrypted +using the current primary encryption provider, enabling automatic migration +to newer keys during updates. Old secrets remain readable via the provider chain. + +> Payload + +```json +{ + "apiVersion": "gateway.api-platform.wso2.com/v1", + "kind": "Secret", + "metadata": { + "name": "database-password" + }, + "spec": { + "displayName": "Database Password", + "description": "PostgreSQL main database password", + "value": "sup3rs3cr3t!" + } +} +``` + +### Authentication + + + +

Parameters

+ +|Name|In|Type|Required|Description| +|---|---|---|---|---| +|body|body|[SecretConfigurationRequest](schemas.md#schemasecretconfigurationrequest)|true|none| +|id|path|string|true|Unique secret identifier| + +> Example responses + +> Secret updated successfully + +```json +{ + "apiVersion": "gateway.api-platform.wso2.com/v1", + "kind": "Secret", + "metadata": { + "name": "database-password" + }, + "spec": { + "displayName": "Database Password", + "description": "PostgreSQL main database password" + }, + "status": { + "id": "database-password", + "createdAt": "2026-01-05T10:30:00Z", + "updatedAt": "2026-01-05T11:45:00Z" + } +} +``` + +> 400 Response + +```json +{ + "status": "error", + "message": "Configuration validation failed", + "errors": [ + { + "field": "spec.context", + "message": "Context must start with / and cannot end with /" + } + ] +} +``` + +

Responses

+ +|Status|Meaning|Description|Schema| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|Secret updated successfully|[SecretConfigurationResponseCreateUpdate](schemas.md#schemasecretconfigurationresponsecreateupdate)| +|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Bad request - missing or invalid value|[ErrorResponse](schemas.md#schemaerrorresponse)| +|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|Unauthorized - authentication required|[ErrorResponse](schemas.md#schemaerrorresponse)| +|404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Secret configuration not found|[ErrorResponse](schemas.md#schemaerrorresponse)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal server error - encryption failed|[ErrorResponse](schemas.md#schemaerrorresponse)| + +## Delete a secret + + + +`DELETE /secrets/{id}` + +> Code samples + +```shell + +curl -X DELETE http://localhost:9090/api/management/v1/secrets/{id} \ + -u {username}:{password} \ + -H 'Accept: application/json' + +``` + +Permanently deletes a secret from the database. This is a hard delete with +no recovery mechanism. The operation is idempotent - deleting a non-existent +secret returns 404. + +### Authentication + + + +

Parameters

+ +|Name|In|Type|Required|Description| +|---|---|---|---|---| +|id|path|string|true|Unique secret identifier| + +> Example responses + +> 401 Response + +```json +{ + "status": "error", + "message": "Configuration validation failed", + "errors": [ + { + "field": "spec.context", + "message": "Context must start with / and cannot end with /" + } + ] +} +``` + +> Secret configuration not found + +```json +{ + "status": "not_found", + "message": "secret configuration not found" +} +``` + +

Responses

+ +|Status|Meaning|Description|Schema| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|Secret deleted successfully (no content)|None| +|401|[Unauthorized](https://tools.ietf.org/html/rfc7235#section-3.1)|Unauthorized - authentication required|[ErrorResponse](schemas.md#schemaerrorresponse)| +|404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|Secret configuration not found|[ErrorResponse](schemas.md#schemaerrorresponse)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal server error - database failure|[ErrorResponse](schemas.md#schemaerrorresponse)| diff --git a/en/docs/api-gateway/1.2.0/gateway-controller-management-api/webbroker-api-management.md b/en/docs/api-gateway/1.2.0/gateway-controller-management-api/webbroker-api-management.md new file mode 100644 index 000000000..fac211b41 --- /dev/null +++ b/en/docs/api-gateway/1.2.0/gateway-controller-management-api/webbroker-api-management.md @@ -0,0 +1,577 @@ +--- +title: "Management API: WebBroker API Management" +description: "REST API reference for creating, listing, retrieving, and deleting WebBroker API configurations in API Platform Gateway." +canonical_url: https://wso2.com/api-platform/docs/api-gateway/gateway-controller-management-api/webbroker-api-management/ +md_url: https://wso2.com/api-platform/docs/api-gateway/gateway-controller-management-api/webbroker-api-management.md +tags: + - api-gateway + - management-api +author: WSO2 API Platform Documentation Team +last_updated: 2026-06-17 +content_type: "reference" +--- + +# WebBroker API Management + +## Create a new WebBrokerAPI + + + +`POST /webbroker-apis` + +> Code samples + +```shell + +curl -X POST http://localhost:9090/api/management/v1/webbroker-apis \ + -u {username}:{password} \ + -H 'Content-Type: application/json' \ + -H 'Accept: application/json' \ + -d @payload.json + +``` + +Add a new WebBrokerAPI to the Gateway. WebBrokerAPI provides bidirectional streaming between WebSocket clients and Kafka brokers with per-connection isolation. + +> Payload + +```json +{ + "apiVersion": "gateway.api-platform.wso2.com/v1", + "kind": "WebBrokerApi", + "metadata": { + "name": "stock-trading-v1.0" + }, + "spec": { + "displayName": "Stock Trading WebBroker API", + "version": "v1.0", + "context": "/stock-trading/$version", + "receiver": { + "name": "websocket-receiver", + "type": "websocket" + }, + "broker": { + "name": "kafka-driver", + "type": "kafka", + "properties": { + "brokers": [ + "kafka-broker-1:9092", + "kafka-broker-2:9092" + ] + } + }, + "allChannels": { + "on_connection_init": { + "policies": [] + }, + "on_produce": { + "policies": [] + }, + "on_consume": { + "policies": [] + } + }, + "channels": { + "prices": { + "produceTo": { + "topic": "stock.prices" + }, + "consumeFrom": { + "topic": "stock.prices" + }, + "on_connection_init": { + "policies": [] + }, + "on_produce": { + "policies": [] + }, + "on_consume": { + "policies": [] + } + } + } + } +} +``` + +### Authentication + + + +

Parameters

+ +|Name|In|Type|Required|Description| +|---|---|---|---|---| +|body|body|[WebBrokerApiRequest](schemas.md#schemawebbrokerapirequest)|true|none| + +> Example responses + +> 201 Response + +```json +{ + "apiVersion": "gateway.api-platform.wso2.com/v1", + "kind": "WebBrokerApi", + "metadata": { + "name": "stock-trading-v1.0" + }, + "spec": { + "displayName": "Stock Trading WebBroker API", + "version": "v1.0", + "context": "/stock-trading/$version", + "receiver": { + "name": "websocket-receiver", + "type": "websocket" + }, + "broker": { + "name": "kafka-driver", + "type": "kafka", + "properties": { + "brokers": [ + "kafka-broker-1:9092", + "kafka-broker-2:9092" + ] + } + }, + "allChannels": { + "on_connection_init": { + "policies": [] + }, + "on_produce": { + "policies": [] + }, + "on_consume": { + "policies": [] + } + }, + "channels": { + "prices": { + "produceTo": { + "topic": "stock.prices" + }, + "consumeFrom": { + "topic": "stock.prices" + }, + "on_connection_init": { + "policies": [] + }, + "on_produce": { + "policies": [] + }, + "on_consume": { + "policies": [] + } + } + } + }, + "status": { + "id": "stock-trading-v1.0", + "state": "deployed", + "createdAt": "2026-04-24T07:21:13Z", + "updatedAt": "2026-04-24T07:21:13Z", + "deployedAt": "2026-04-24T07:21:13Z" + } +} +``` + +

Responses

+ +|Status|Meaning|Description|Schema| +|---|---|---|---| +|201|[Created](https://tools.ietf.org/html/rfc7231#section-6.3.2)|WebBrokerAPI created successfully|[WebBrokerApi](schemas.md#schemawebbrokerapi)| +|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Invalid configuration (validation failed)|[ErrorResponse](schemas.md#schemaerrorresponse)| +|409|[Conflict](https://tools.ietf.org/html/rfc7231#section-6.5.8)|Conflict - WebBroker API with same name and version already exists|[ErrorResponse](schemas.md#schemaerrorresponse)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal server error|[ErrorResponse](schemas.md#schemaerrorresponse)| + +## List all WebBrokerAPIs + + + +`GET /webbroker-apis` + +> Code samples + +```shell + +curl -X GET http://localhost:9090/api/management/v1/webbroker-apis \ + -u {username}:{password} \ + -H 'Accept: application/json' + +``` + +List WebBrokerAPIs registered in the Gateway, optionally filtered by name, version, or status. + +### Authentication + + + +

Parameters

+ +|Name|In|Type|Required|Description| +|---|---|---|---|---| +|displayName|query|string|false|Filter by WebBroker API display name| +|version|query|string|false|Filter by WebBroker API version| +|status|query|string|false|Filter by deployment status| + +#### Enumerated Values + +|Parameter|Value| +|---|---| +|status|deployed| +|status|undeployed| + +> Example responses + +> 200 Response + +```json +{ + "status": "success", + "count": 3, + "apis": [ + { + "apiVersion": "gateway.api-platform.wso2.com/v1", + "kind": "WebBrokerApi", + "metadata": { + "name": "stock-trading-v1.0" + }, + "spec": { + "displayName": "Stock Trading WebBroker API", + "version": "v1.0", + "context": "/stock-trading/$version", + "receiver": { + "name": "websocket-receiver", + "type": "websocket" + }, + "broker": { + "name": "kafka-driver", + "type": "kafka", + "properties": { + "brokers": [ + "kafka-broker-1:9092", + "kafka-broker-2:9092" + ] + } + }, + "allChannels": { + "on_connection_init": { + "policies": [] + }, + "on_produce": { + "policies": [] + }, + "on_consume": { + "policies": [] + } + }, + "channels": { + "prices": { + "produceTo": { + "topic": "stock.prices" + }, + "consumeFrom": { + "topic": "stock.prices" + }, + "on_connection_init": { + "policies": [] + }, + "on_produce": { + "policies": [] + }, + "on_consume": { + "policies": [] + } + } + } + }, + "status": { + "id": "stock-trading-v1.0", + "state": "deployed", + "createdAt": "2026-04-24T07:21:13Z", + "updatedAt": "2026-04-24T07:21:13Z", + "deployedAt": "2026-04-24T07:21:13Z" + } + } + ] +} +``` + +

Responses

+ +|Status|Meaning|Description|Schema| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|List of WebBrokerAPIs|Inline| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal server error|[ErrorResponse](schemas.md#schemaerrorresponse)| + +

Response Schema

+ +Status Code **200** + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|status|string|false|none|none| +|count|integer|false|none|none| +|apis|[allOf]|false|none|none| + +*allOf* + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|*anonymous*|[WebBrokerApiRequest](schemas.md#schemawebbrokerapirequest)|false|none|none| +|apiVersion|string|true|none|API specification version| +|kind|string|true|none|API type| +|metadata|[Metadata](schemas.md#schemametadata)|true|none|none| +|name|string|true|none|Unique handle for the resource| +|labels|object|false|none|Labels are key-value pairs for organizing and selecting APIs. Keys must not contain spaces.| +|**additionalProperties**|string|false|none|none| +|annotations|object|false|none|Annotations are arbitrary non-identifying metadata. Use domain-prefixed keys.| +|**additionalProperties**|string|false|none|none| +|spec|[WebBrokerApiData](schemas.md#schemawebbrokerapidata)|true|none|none| +|displayName|string|true|none|Human-readable API name (must be URL-friendly - only letters, numbers, spaces, hyphens, underscores, and dots allowed)| +|version|string|true|none|Semantic version of the API| +|context|string|true|none|Base path for all API routes (must start with /, no trailing slash)| +|receiver|[WebBrokerApiReceiver](schemas.md#schemawebbrokerapireceiver)|true|none|WebSocket receiver configuration| +|name|string|true|none|Receiver name| +|type|string|true|none|Receiver type| +|properties|object|false|none|Additional receiver properties| +|broker|[WebBrokerApiBroker](schemas.md#schemawebbrokerapibroker)|true|none|Message broker driver configuration| +|name|string|true|none|Broker driver name| +|type|string|true|none|Broker driver type| +|properties|object|true|none|Broker driver properties (e.g., bootstrap servers)| +|allChannels|[WebBrokerApiAllChannelPolicies](schemas.md#schemawebbrokerapiallchannelpolicies)|false|none|Protocol mediation policies applied to all channels| +|on_connection_init|[WebBrokerApiPolicyGroup](schemas.md#schemawebbrokerapipolicygroup)|false|none|Group of policies| +|policies|[[Policy](schemas.md#schemapolicy)]|false|none|List of policies to apply| +|name|string|true|none|Name of the policy| +|version|string|true|none|Version of the policy. Only major-only version is allowed (e.g., v0, v1). Full semantic version (e.g., v1.0.0) is not accepted and will be rejected. The Gateway Controller resolves the major version to the single matching full version installed in the gateway image.| +|executionCondition|string|false|none|Expression controlling conditional execution of the policy| +|params|object|false|none|Arbitrary parameters for the policy (free-form key/value structure)| +|on_produce|[WebBrokerApiPolicyGroup](schemas.md#schemawebbrokerapipolicygroup)|false|none|Group of policies| +|on_consume|[WebBrokerApiPolicyGroup](schemas.md#schemawebbrokerapipolicygroup)|false|none|Group of policies| +|channels|object|true|none|Map of WebSocket channels for bidirectional streaming with Kafka (key is channel name)| +|**additionalProperties**|[WebBrokerApiChannel](schemas.md#schemawebbrokerapichannel)|false|none|WebSocket channel configuration with Kafka topic mapping| +|produceTo|[WebBrokerApiProduceConfig](schemas.md#schemawebbrokerapiproduceconfig)|false|none|Configuration for producing messages from WebSocket to Kafka| +|topic|string|true|none|Kafka topic to produce messages to| +|consumeFrom|[WebBrokerApiConsumeConfig](schemas.md#schemawebbrokerapiconsumeconfig)|false|none|Configuration for consuming messages from Kafka to WebSocket| +|topic|string|true|none|Kafka topic to consume messages from| +|on_connection_init|[WebBrokerApiPolicyGroup](schemas.md#schemawebbrokerapipolicygroup)|false|none|Group of policies| +|on_produce|[WebBrokerApiPolicyGroup](schemas.md#schemawebbrokerapipolicygroup)|false|none|Group of policies| +|on_consume|[WebBrokerApiPolicyGroup](schemas.md#schemawebbrokerapipolicygroup)|false|none|Group of policies| +|vhosts|object|false|none|Custom virtual hosts/domains for the API| +|main|string|true|none|Custom virtual host/domain for production traffic| +|sandbox|string|false|none|Custom virtual host/domain for sandbox traffic| +|deploymentState|string|false|none|Desired deployment state - 'deployed' (default) or 'undeployed'. When set to 'undeployed', the API is removed from router traffic but configuration and policies are preserved for potential redeployment.| + +*and* + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|*anonymous*|object|false|none|none| +|status|[ResourceStatus](schemas.md#schemaresourcestatus)|false|read-only|Server-managed lifecycle fields. Populated on responses.| +|id|string|false|none|Unique identifier assigned by the server (equal to metadata.name)| +|state|string|false|none|Desired deployment state reported by the server| +|createdAt|string(date-time)|false|none|Timestamp when the resource was first created (UTC)| +|updatedAt|string(date-time)|false|none|Timestamp when the resource was last updated (UTC)| +|deployedAt|string(date-time)|false|none|Timestamp when the resource was last deployed (omitted when undeployed)| + +#### Enumerated Values + +|Property|Value| +|---|---| +|apiVersion|gateway.api-platform.wso2.com/v1| +|kind|WebBrokerApi| +|deploymentState|deployed| +|deploymentState|undeployed| +|state|deployed| +|state|undeployed| + +## Get WebBrokerAPI by id + + + +`GET /webbroker-apis/{id}` + +> Code samples + +```shell + +curl -X GET http://localhost:9090/api/management/v1/webbroker-apis/{id} \ + -u {username}:{password} \ + -H 'Accept: application/json' + +``` + +Get a WebBrokerAPI by its ID. + +### Authentication + + + +

Parameters

+ +|Name|In|Type|Required|Description| +|---|---|---|---|---| +|id|path|string|true|Unique public identifier for the WebBroker API.| + +#### Detailed descriptions + +**id**: Unique public identifier for the WebBroker API. + +> Example responses + +> 200 Response + +```json +{ + "apiVersion": "gateway.api-platform.wso2.com/v1", + "kind": "WebBrokerApi", + "metadata": { + "name": "stock-trading-v1.0" + }, + "spec": { + "displayName": "Stock Trading WebBroker API", + "version": "v1.0", + "context": "/stock-trading/$version", + "receiver": { + "name": "websocket-receiver", + "type": "websocket" + }, + "broker": { + "name": "kafka-driver", + "type": "kafka", + "properties": { + "brokers": [ + "kafka-broker-1:9092", + "kafka-broker-2:9092" + ] + } + }, + "allChannels": { + "on_connection_init": { + "policies": [] + }, + "on_produce": { + "policies": [] + }, + "on_consume": { + "policies": [] + } + }, + "channels": { + "prices": { + "produceTo": { + "topic": "stock.prices" + }, + "consumeFrom": { + "topic": "stock.prices" + }, + "on_connection_init": { + "policies": [] + }, + "on_produce": { + "policies": [] + }, + "on_consume": { + "policies": [] + } + } + } + }, + "status": { + "id": "stock-trading-v1.0", + "state": "deployed", + "createdAt": "2026-04-24T07:21:13Z", + "updatedAt": "2026-04-24T07:21:13Z", + "deployedAt": "2026-04-24T07:21:13Z" + } +} +``` + +

Responses

+ +|Status|Meaning|Description|Schema| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|WebBrokerAPI details|[WebBrokerApi](schemas.md#schemawebbrokerapi)| +|404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|WebBrokerAPI not found|[ErrorResponse](schemas.md#schemaerrorresponse)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal server error|[ErrorResponse](schemas.md#schemaerrorresponse)| + +## Delete a WebBrokerAPI + + + +`DELETE /webbroker-apis/{id}` + +> Code samples + +```shell + +curl -X DELETE http://localhost:9090/api/management/v1/webbroker-apis/{id} \ + -u {username}:{password} \ + -H 'Accept: application/json' + +``` + +Delete a WebBrokerAPI from the Gateway. + +### Authentication + + + +

Parameters

+ +|Name|In|Type|Required|Description| +|---|---|---|---|---| +|id|path|string|true|Unique public identifier of the WebBroker API to delete.| + +#### Detailed descriptions + +**id**: Unique public identifier of the WebBroker API to delete. + +> Example responses + +> 200 Response + +```json +{ + "status": "success", + "message": "WebBrokerAPI deleted successfully", + "id": "stock-trading-webbroker-api" +} +``` + +

Responses

+ +|Status|Meaning|Description|Schema| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|WebBrokerAPI deleted successfully|Inline| +|404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|WebBrokerAPI not found|[ErrorResponse](schemas.md#schemaerrorresponse)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal server error|[ErrorResponse](schemas.md#schemaerrorresponse)| + +

Response Schema

+ +Status Code **200** + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|status|string|false|none|none| +|message|string|false|none|none| +|id|string|false|none|none| \ No newline at end of file diff --git a/en/docs/api-gateway/1.2.0/gateway-controller-management-api/websub-api-management.md b/en/docs/api-gateway/1.2.0/gateway-controller-management-api/websub-api-management.md new file mode 100644 index 000000000..0cf7177f3 --- /dev/null +++ b/en/docs/api-gateway/1.2.0/gateway-controller-management-api/websub-api-management.md @@ -0,0 +1,893 @@ +--- +title: "Management API: WebSub API Management" +description: "REST API reference for creating, listing, updating, and deleting WebSub API configurations and API keys in API Platform Gateway." +canonical_url: https://wso2.com/api-platform/docs/api-gateway/gateway-controller-management-api/websub-api-management/ +md_url: https://wso2.com/api-platform/docs/api-gateway/gateway-controller-management-api/websub-api-management.md +tags: + - api-gateway + - management-api +author: WSO2 API Platform Documentation Team +last_updated: 2026-06-17 +content_type: "reference" +--- + +# WebSub API Management + +## Create a new WebSubAPI + + + +`POST /websub-apis` + +> Code samples + +```shell + +curl -X POST http://localhost:9090/api/management/v1/websub-apis \ + -u {username}:{password} \ + -H 'Content-Type: application/json' \ + -H 'Accept: application/json' \ + -d @payload.json + +``` + +Add a new WebSubAPI to the Gateway. + +> Payload + +```json +{ + "apiVersion": "gateway.api-platform.wso2.com/v1", + "kind": "WebSubApi", + "metadata": { + "name": "github-events-v1.0" + }, + "spec": { + "displayName": "GitHub Events", + "version": "v1.0", + "context": "/github-events/$version", + "channels": [ + { + "name": "issues", + "method": "SUB" + }, + { + "name": "pull_requests", + "method": "SUB" + } + ] + } +} +``` + +### Authentication + + + +

Parameters

+ +|Name|In|Type|Required|Description| +|---|---|---|---|---| +|body|body|[WebSubAPIRequest](schemas.md#schemawebsubapirequest)|true|none| + +> Example responses + +> 201 Response + +```json +{ + "apiVersion": "gateway.api-platform.wso2.com/v1", + "kind": "WebSubApi", + "metadata": { + "name": "github-events-v1.0" + }, + "spec": { + "displayName": "GitHub Events", + "version": "v1.0", + "context": "/github-events/$version", + "channels": [ + { + "name": "issues", + "method": "SUB" + }, + { + "name": "pull_requests", + "method": "SUB" + } + ] + }, + "status": { + "id": "github-events-v1.0", + "state": "deployed", + "createdAt": "2026-04-24T07:21:13Z", + "updatedAt": "2026-04-24T07:21:13Z", + "deployedAt": "2026-04-24T07:21:13Z" + } +} +``` + +

Responses

+ +|Status|Meaning|Description|Schema| +|---|---|---|---| +|201|[Created](https://tools.ietf.org/html/rfc7231#section-6.3.2)|WebSubAPI created successfully|[WebSubAPI](schemas.md#schemawebsubapi)| +|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Invalid configuration (validation failed)|[ErrorResponse](schemas.md#schemaerrorresponse)| +|409|[Conflict](https://tools.ietf.org/html/rfc7231#section-6.5.8)|Conflict - WebSub API with same name and version already exists|[ErrorResponse](schemas.md#schemaerrorresponse)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal server error|[ErrorResponse](schemas.md#schemaerrorresponse)| + +## List all WebSubAPIs + + + +`GET /websub-apis` + +> Code samples + +```shell + +curl -X GET http://localhost:9090/api/management/v1/websub-apis \ + -u {username}:{password} \ + -H 'Accept: application/json' + +``` + +List WebSubAPIs registered in the Gateway, optionally filtered by name, version, context, or status. + +### Authentication + + + +

Parameters

+ +|Name|In|Type|Required|Description| +|---|---|---|---|---| +|displayName|query|string|false|Filter by WebSub API display name| +|version|query|string|false|Filter by WebSub API version| +|context|query|string|false|Filter by WebSub API context/path| +|status|query|string|false|Filter by deployment status| + +#### Enumerated Values + +|Parameter|Value| +|---|---| +|status|deployed| +|status|undeployed| + +> Example responses + +> 200 Response + +```json +{ + "status": "success", + "count": 5, + "apis": [ + { + "apiVersion": "gateway.api-platform.wso2.com/v1", + "kind": "WebSubApi", + "metadata": { + "name": "github-events-v1.0" + }, + "spec": { + "displayName": "GitHub Events", + "version": "v1.0", + "context": "/github-events/$version", + "channels": [ + { + "name": "issues", + "method": "SUB" + }, + { + "name": "pull_requests", + "method": "SUB" + } + ] + }, + "status": { + "id": "github-events-v1.0", + "state": "deployed", + "createdAt": "2026-04-24T07:21:13Z", + "updatedAt": "2026-04-24T07:21:13Z", + "deployedAt": "2026-04-24T07:21:13Z" + } + } + ] +} +``` + +

Responses

+ +|Status|Meaning|Description|Schema| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|List of WebSubAPIs|Inline| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal server error|[ErrorResponse](schemas.md#schemaerrorresponse)| + +

Response Schema

+ +Status Code **200** + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|status|string|false|none|none| +|count|integer|false|none|none| +|apis|[allOf]|false|none|none| + +*allOf* + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|*anonymous*|[WebSubAPIRequest](schemas.md#schemawebsubapirequest)|false|none|none| +|apiVersion|string|true|none|API specification version| +|kind|string|true|none|API type| +|metadata|[Metadata](schemas.md#schemametadata)|true|none|none| +|name|string|true|none|Unique handle for the resource| +|labels|object|false|none|Labels are key-value pairs for organizing and selecting APIs. Keys must not contain spaces.| +|**additionalProperties**|string|false|none|none| +|annotations|object|false|none|Annotations are arbitrary non-identifying metadata. Use domain-prefixed keys.| +|**additionalProperties**|string|false|none|none| +|spec|[WebhookAPIData](schemas.md#schemawebhookapidata)|true|none|none| +|displayName|string|true|none|Human-readable API name (must be URL-friendly - only letters, numbers, spaces, hyphens, underscores, and dots allowed)| +|version|string|true|none|Semantic version of the API| +|context|string|true|none|Base path for all API routes (must start with /, no trailing slash)| +|vhosts|object|false|none|Custom virtual hosts/domains for the API| +|main|string|true|none|Custom virtual host/domain for production traffic| +|sandbox|string|false|none|Custom virtual host/domain for sandbox traffic| +|allChannels|[WebSubAllChannelPolicies](schemas.md#schemawebsuballchannelpolicies)|false|none|Policies applied to all channels, organized by event type.| +|on_subscription|[WebSubEventPolicies](schemas.md#schemawebsubeventpolicies)|false|none|Policies for a single event type.| +|policies|[[Policy](schemas.md#schemapolicy)]|false|none|List of policies applied for this event type.| +|name|string|true|none|Name of the policy| +|version|string|true|none|Version of the policy. Only major-only version is allowed (e.g., v0, v1). Full semantic version (e.g., v1.0.0) is not accepted and will be rejected. The Gateway Controller resolves the major version to the single matching full version installed in the gateway image.| +|executionCondition|string|false|none|Expression controlling conditional execution of the policy| +|params|object|false|none|Arbitrary parameters for the policy (free-form key/value structure)| +|on_unsubscription|[WebSubEventPolicies](schemas.md#schemawebsubeventpolicies)|false|none|Policies for a single event type.| +|on_message_received|[WebSubEventPolicies](schemas.md#schemawebsubeventpolicies)|false|none|Policies for a single event type.| +|on_message_delivery|[WebSubEventPolicies](schemas.md#schemawebsubeventpolicies)|false|none|Policies for a single event type.| +|channels|object|false|none|Per-channel configuration keyed by channel name. Each key is a channel name and defines policies applied only to that channel.| +|**additionalProperties**|[WebSubChannel](schemas.md#schemawebsubchannel)|false|none|A single channel definition with optional per-channel policy overrides.| +|on_subscription|[WebSubEventPolicies](schemas.md#schemawebsubeventpolicies)|false|none|Policies for a single event type.| +|on_unsubscription|[WebSubEventPolicies](schemas.md#schemawebsubeventpolicies)|false|none|Policies for a single event type.| +|on_message_received|[WebSubEventPolicies](schemas.md#schemawebsubeventpolicies)|false|none|Policies for a single event type.| +|on_message_delivery|[WebSubEventPolicies](schemas.md#schemawebsubeventpolicies)|false|none|Policies for a single event type.| +|deploymentState|string|false|none|Desired deployment state - 'deployed' (default) or 'undeployed'. When set to 'undeployed', the API is removed from router traffic but configuration, API keys, and policies are preserved for potential redeployment.| + +*and* + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|*anonymous*|object|false|none|none| +|status|[ResourceStatus](schemas.md#schemaresourcestatus)|false|read-only|Server-managed lifecycle fields. Populated on responses.| +|id|string|false|none|Unique identifier assigned by the server (equal to metadata.name)| +|state|string|false|none|Desired deployment state reported by the server| +|createdAt|string(date-time)|false|none|Timestamp when the resource was first created (UTC)| +|updatedAt|string(date-time)|false|none|Timestamp when the resource was last updated (UTC)| +|deployedAt|string(date-time)|false|none|Timestamp when the resource was last deployed (omitted when undeployed)| + +#### Enumerated Values + +|Property|Value| +|---|---| +|apiVersion|gateway.api-platform.wso2.com/v1| +|kind|WebSubApi| +|deploymentState|deployed| +|deploymentState|undeployed| +|state|deployed| +|state|undeployed| + +## Create a new API key for a WebSub API + + + +`POST /websub-apis/{id}/api-keys` + +> Code samples + +```shell + +curl -X POST http://localhost:9090/api/management/v1/websub-apis/{id}/api-keys \ + -u {username}:{password} \ + -H 'Content-Type: application/json' \ + -H 'Accept: application/json' \ + -d @payload.json + +``` + +Generate a new API key for a WebSub API in the Gateway. The key is a 32-byte random value encoded in hexadecimal, prefixed with `apip_`. Use the API Key policy on the API to validate incoming requests with this key. + +> Payload + +```json +{ + "name": "my-production-key" +} +``` + +### Authentication + + + +

Parameters

+ +|Name|In|Type|Required|Description| +|---|---|---|---|---| +|id|path|string|true|Unique public identifier of the WebSub API to generate the key for| +|body|body|[APIKeyCreationRequest](schemas.md#schemaapikeycreationrequest)|true|none| + +> Example responses + +> 201 Response + +```json +{ + "status": "success", + "message": "API key generated successfully", + "remainingApiKeyQuota": 9, + "apiKey": { + "name": "my-production-key", + "displayName": "My Production Key", + "apiKey": "apip_1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef", + "apiId": "reading-list-api-v1.0", + "status": "active", + "createdAt": "2026-04-01T10:30:00Z", + "createdBy": "admin", + "expiresAt": null, + "source": "local" + } +} +``` + +

Responses

+ +|Status|Meaning|Description|Schema| +|---|---|---|---| +|201|[Created](https://tools.ietf.org/html/rfc7231#section-6.3.2)|API key created successfully|[APIKeyCreationResponse](schemas.md#schemaapikeycreationresponse)| +|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Invalid configuration (validation failed)|[ErrorResponse](schemas.md#schemaerrorresponse)| +|404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|WebSub API not found|[ErrorResponse](schemas.md#schemaerrorresponse)| +|409|[Conflict](https://tools.ietf.org/html/rfc7231#section-6.5.8)|Conflict (duplicate key or conflicting update)|[ErrorResponse](schemas.md#schemaerrorresponse)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal server error|[ErrorResponse](schemas.md#schemaerrorresponse)| + +## Get the list of API keys for a WebSub API + + + +`GET /websub-apis/{id}/api-keys` + +> Code samples + +```shell + +curl -X GET http://localhost:9090/api/management/v1/websub-apis/{id}/api-keys \ + -u {username}:{password} \ + -H 'Accept: application/json' + +``` + +List all API keys for a WebSub API in the Gateway. + +### Authentication + + + +

Parameters

+ +|Name|In|Type|Required|Description| +|---|---|---|---|---| +|id|path|string|true|Unique public identifier of the WebSub API to retrieve the keys for| + +> Example responses + +> 200 Response + +```json +{ + "apiKeys": [ + { + "name": "my-production-key", + "displayName": "My Production Key", + "apiKey": "apip_1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef", + "apiId": "reading-list-api-v1.0", + "status": "active", + "createdAt": "2026-04-01T10:30:00Z", + "createdBy": "admin", + "expiresAt": null, + "source": "local" + } + ], + "totalCount": 3, + "status": "success" +} +``` + +

Responses

+ +|Status|Meaning|Description|Schema| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|List of API keys|[APIKeyListResponse](schemas.md#schemaapikeylistresponse)| +|404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|WebSub API not found|[ErrorResponse](schemas.md#schemaerrorresponse)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal server error|[ErrorResponse](schemas.md#schemaerrorresponse)| + +## Regenerate API key for a WebSub API + + + +`POST /websub-apis/{id}/api-keys/{apiKeyName}/regenerate` + +> Code samples + +```shell + +curl -X POST http://localhost:9090/api/management/v1/websub-apis/{id}/api-keys/{apiKeyName}/regenerate \ + -u {username}:{password} \ + -H 'Content-Type: application/json' \ + -H 'Accept: application/json' \ + -d @payload.json + +``` + +Regenerate an existing API key for a WebSub API in the Gateway. The previous key is revoked and replaced with a new 32-byte random value encoded in hexadecimal, prefixed with `apip_`. + +> Payload + +```json +{} +``` + +### Authentication + + + +

Parameters

+ +|Name|In|Type|Required|Description| +|---|---|---|---|---| +|id|path|string|true|Unique public identifier of the WebSub API| +|apiKeyName|path|string|true|Name of the API key to regenerate| +|body|body|[APIKeyRegenerationRequest](schemas.md#schemaapikeyregenerationrequest)|true|none| + +> Example responses + +> 200 Response + +```json +{ + "status": "success", + "message": "API key generated successfully", + "remainingApiKeyQuota": 9, + "apiKey": { + "name": "my-production-key", + "displayName": "My Production Key", + "apiKey": "apip_1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef", + "apiId": "reading-list-api-v1.0", + "status": "active", + "createdAt": "2026-04-01T10:30:00Z", + "createdBy": "admin", + "expiresAt": null, + "source": "local" + } +} +``` + +

Responses

+ +|Status|Meaning|Description|Schema| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|API key rotated successfully|[APIKeyCreationResponse](schemas.md#schemaapikeycreationresponse)| +|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Invalid configuration (validation failed)|[ErrorResponse](schemas.md#schemaerrorresponse)| +|404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|WebSub API or API key not found|[ErrorResponse](schemas.md#schemaerrorresponse)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal server error|[ErrorResponse](schemas.md#schemaerrorresponse)| + +## Update an API key for a WebSub API + + + +`PUT /websub-apis/{id}/api-keys/{apiKeyName}` + +> Code samples + +```shell + +curl -X PUT http://localhost:9090/api/management/v1/websub-apis/{id}/api-keys/{apiKeyName} \ + -u {username}:{password} \ + -H 'Content-Type: application/json' \ + -H 'Accept: application/json' \ + -d @payload.json + +``` + +Update an API key with a custom value instead of auto-generating one. + +> Payload + +```json +{ + "name": "my-production-key" +} +``` + +### Authentication + + + +

Parameters

+ +|Name|In|Type|Required|Description| +|---|---|---|---|---| +|id|path|string|true|Unique public identifier of the WebSub API| +|apiKeyName|path|string|true|Name of the API key to update| +|body|body|[APIKeyUpdateRequest](schemas.md#schemaapikeyupdaterequest)|true|none| + +> Example responses + +> 200 Response + +```json +{ + "status": "success", + "message": "API key generated successfully", + "remainingApiKeyQuota": 9, + "apiKey": { + "name": "my-production-key", + "displayName": "My Production Key", + "apiKey": "apip_1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef", + "apiId": "reading-list-api-v1.0", + "status": "active", + "createdAt": "2026-04-01T10:30:00Z", + "createdBy": "admin", + "expiresAt": null, + "source": "local" + } +} +``` + +

Responses

+ +|Status|Meaning|Description|Schema| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|API key updated successfully|[APIKeyCreationResponse](schemas.md#schemaapikeycreationresponse)| +|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Invalid request (validation failed)|[ErrorResponse](schemas.md#schemaerrorresponse)| +|404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|WebSub API or API key not found|[ErrorResponse](schemas.md#schemaerrorresponse)| +|409|[Conflict](https://tools.ietf.org/html/rfc7231#section-6.5.8)|Conflict (duplicate key or conflicting update)|[ErrorResponse](schemas.md#schemaerrorresponse)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal server error|[ErrorResponse](schemas.md#schemaerrorresponse)| + +## Revoke an API key for a WebSub API + + + +`DELETE /websub-apis/{id}/api-keys/{apiKeyName}` + +> Code samples + +```shell + +curl -X DELETE http://localhost:9090/api/management/v1/websub-apis/{id}/api-keys/{apiKeyName} \ + -u {username}:{password} \ + -H 'Accept: application/json' + +``` + +Revoke an API key. Once revoked, it can no longer be used to authenticate requests. + +### Authentication + + + +

Parameters

+ +|Name|In|Type|Required|Description| +|---|---|---|---|---| +|id|path|string|true|Unique public identifier of the WebSub API| +|apiKeyName|path|string|true|Name of the API key to revoke| + +> Example responses + +> 200 Response + +```json +{ + "status": "success", + "message": "API key revoked successfully" +} +``` + +

Responses

+ +|Status|Meaning|Description|Schema| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|API key revoked successfully|[APIKeyRevocationResponse](schemas.md#schemaapikeyrevocationresponse)| +|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Invalid configuration (validation failed)|[ErrorResponse](schemas.md#schemaerrorresponse)| +|404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|WebSub API or API key not found|[ErrorResponse](schemas.md#schemaerrorresponse)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal server error|[ErrorResponse](schemas.md#schemaerrorresponse)| + +## Get WebSubAPI by id + + + +`GET /websub-apis/{id}` + +> Code samples + +```shell + +curl -X GET http://localhost:9090/api/management/v1/websub-apis/{id} \ + -u {username}:{password} \ + -H 'Accept: application/json' + +``` + +Get a WebSubAPI by its ID. + +### Authentication + + + +

Parameters

+ +|Name|In|Type|Required|Description| +|---|---|---|---|---| +|id|path|string|true|Unique public identifier for the WebSub API.| + +#### Detailed descriptions + +**id**: Unique public identifier for the WebSub API. + +> Example responses + +> 200 Response + +```json +{ + "apiVersion": "gateway.api-platform.wso2.com/v1", + "kind": "WebSubApi", + "metadata": { + "name": "github-events-v1.0" + }, + "spec": { + "displayName": "GitHub Events", + "version": "v1.0", + "context": "/github-events/$version", + "channels": [ + { + "name": "issues", + "method": "SUB" + }, + { + "name": "pull_requests", + "method": "SUB" + } + ] + }, + "status": { + "id": "github-events-v1.0", + "state": "deployed", + "createdAt": "2026-04-24T07:21:13Z", + "updatedAt": "2026-04-24T07:21:13Z", + "deployedAt": "2026-04-24T07:21:13Z" + } +} +``` + +

Responses

+ +|Status|Meaning|Description|Schema| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|WebSubAPI details|[WebSubAPI](schemas.md#schemawebsubapi)| +|404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|WebSubAPI not found|[ErrorResponse](schemas.md#schemaerrorresponse)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal server error|[ErrorResponse](schemas.md#schemaerrorresponse)| + +## Update an existing WebSubAPI + + + +`PUT /websub-apis/{id}` + +> Code samples + +```shell + +curl -X PUT http://localhost:9090/api/management/v1/websub-apis/{id} \ + -u {username}:{password} \ + -H 'Content-Type: application/json' \ + -H 'Accept: application/json' \ + -d @payload.json + +``` + +Update an existing WebSubAPI in the Gateway. + +> Payload + +```json +{ + "apiVersion": "gateway.api-platform.wso2.com/v1", + "kind": "WebSubApi", + "metadata": { + "name": "github-events-v1.0" + }, + "spec": { + "displayName": "GitHub Events", + "version": "v1.0", + "context": "/github-events/$version", + "channels": [ + { + "name": "issues", + "method": "SUB" + }, + { + "name": "pull_requests", + "method": "SUB" + } + ] + } +} +``` + +### Authentication + + + +

Parameters

+ +|Name|In|Type|Required|Description| +|---|---|---|---|---| +|id|path|string|true|Unique public identifier of the WebSub API to update.| +|body|body|[WebSubAPIRequest](schemas.md#schemawebsubapirequest)|true|none| + +#### Detailed descriptions + +**id**: Unique public identifier of the WebSub API to update. + +> Example responses + +> 200 Response + +```json +{ + "apiVersion": "gateway.api-platform.wso2.com/v1", + "kind": "WebSubApi", + "metadata": { + "name": "github-events-v1.0" + }, + "spec": { + "displayName": "GitHub Events", + "version": "v1.0", + "context": "/github-events/$version", + "channels": [ + { + "name": "issues", + "method": "SUB" + }, + { + "name": "pull_requests", + "method": "SUB" + } + ] + }, + "status": { + "id": "github-events-v1.0", + "state": "deployed", + "createdAt": "2026-04-24T07:21:13Z", + "updatedAt": "2026-04-24T07:21:13Z", + "deployedAt": "2026-04-24T07:21:13Z" + } +} +``` + +

Responses

+ +|Status|Meaning|Description|Schema| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|WebSubAPI updated successfully|[WebSubAPI](schemas.md#schemawebsubapi)| +|400|[Bad Request](https://tools.ietf.org/html/rfc7231#section-6.5.1)|Invalid configuration (validation failed)|[ErrorResponse](schemas.md#schemaerrorresponse)| +|404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|WebSubAPI not found|[ErrorResponse](schemas.md#schemaerrorresponse)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal server error|[ErrorResponse](schemas.md#schemaerrorresponse)| + +## Delete a WebSubAPI + + + +`DELETE /websub-apis/{id}` + +> Code samples + +```shell + +curl -X DELETE http://localhost:9090/api/management/v1/websub-apis/{id} \ + -u {username}:{password} \ + -H 'Accept: application/json' + +``` + +Delete a WebSubAPI from the Gateway. + +### Authentication + + + +

Parameters

+ +|Name|In|Type|Required|Description| +|---|---|---|---|---| +|id|path|string|true|Unique public identifier of the WebSub API to delete.| + +#### Detailed descriptions + +**id**: Unique public identifier of the WebSub API to delete. + +> Example responses + +> 200 Response + +```json +{ + "status": "success", + "message": "WebSubAPI deleted successfully", + "id": "weather-websub-api" +} +``` + +

Responses

+ +|Status|Meaning|Description|Schema| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|WebSubAPI deleted successfully|Inline| +|404|[Not Found](https://tools.ietf.org/html/rfc7231#section-6.5.4)|WebSubAPI not found|[ErrorResponse](schemas.md#schemaerrorresponse)| +|500|[Internal Server Error](https://tools.ietf.org/html/rfc7231#section-6.6.1)|Internal server error|[ErrorResponse](schemas.md#schemaerrorresponse)| + +

Response Schema

+ +Status Code **200** + +|Name|Type|Required|Restrictions|Description| +|---|---|---|---|---| +|status|string|false|none|none| +|message|string|false|none|none| +|id|string|false|none|none| \ No newline at end of file diff --git a/en/docs/api-gateway/1.2.0/observability/logging.md b/en/docs/api-gateway/1.2.0/observability/logging.md new file mode 100644 index 000000000..7b7ccace1 --- /dev/null +++ b/en/docs/api-gateway/1.2.0/observability/logging.md @@ -0,0 +1,348 @@ +--- +title: "Configure Centralized Logging" +description: "Set up centralized log collection for API Platform Gateway using Fluent Bit, OpenSearch, and OpenSearch Dashboards." +canonical_url: https://wso2.com/api-platform/docs/api-gateway/observability/logging/ +md_url: https://wso2.com/api-platform/docs/api-gateway/observability/logging.md +tags: + - api-gateway + - observability + - logging +author: WSO2 API Platform Documentation Team +last_updated: 2026-06-17 +content_type: "how-to" +--- + +# Gateway Logging + +This guide explains how to implement and configure logging for the API Platform Gateway components. + +## Overview + +The default logging services included in the Docker Compose configuration are **demonstration services** designed to showcase how you can observe component logs in a centralized setup. These services provide a reference implementation that you can use out-of-the-box for development, testing, or as a starting point for your production logging strategy. + +**Important**: You are free to choose any logging or observability strategy that suits your environment and requirements. The provided setup is just one of many possible configurations. + +## Logging Architecture + +The default logging stack consists of: + +- **Fluent Bit**: Lightweight log collector that reads Docker container logs and forwards them to OpenSearch +- **OpenSearch**: Stores and indexes log data for searchability and analysis +- **OpenSearch Dashboards**: Web interface for visualizing, exploring, and searching logs + +### How It Works + +1. Gateway components (gateway-controller, policy-engine, router) write structured JSON logs to stdout/stderr +2. Docker captures these logs and stores them in `/var/lib/docker/containers` +3. Fluent Bit tails these log files, parses them, and enriches them with metadata (component name, hostname) +4. Fluent Bit forwards processed logs to OpenSearch +5. Users can view and search logs through OpenSearch Dashboards + +## Enabling Logging Services + +### Gateway Components Already Log to Standard Output + +**No special configuration is required to enable logging in the gateway components.** All gateway components (gateway-controller, policy-engine, and router) follow the [12-factor app](https://12factor.net/logs) architecture principle for logging: + +- Components write all logs to **stdout (standard output)** and **stderr (standard error)** +- Logs are emitted as structured JSON for easy parsing +- No file-based logging or log management is built into the components + +This architecture approach allows you to utilize **any industry-standard logging stack** to collect logs from Docker container log files and view them in your preferred observability platform. The gateway components are completely decoupled from the logging infrastructure. + +### Demonstrated Logging Services + +The logging services included in the Docker Compose file (OpenSearch, OpenSearch Dashboards, and Fluent Bit) are provided as **demonstration services** to show one possible way to collect and visualize logs. You can use them as-is for development/testing, or replace them with your own logging solution. + +The gateway uses Docker Compose profiles to optionally enable these demonstration logging services. + +### Start Gateway with Demonstrated Logging Services + +To start the gateway with the demonstration logging services enabled: + +```bash +docker compose --profile logging up -d +``` + +This starts: +- Core gateway services (gateway-controller, policy-engine, router) - *which log to stdout/stderr* +- OpenSearch - *stores and indexes logs* +- OpenSearch Dashboards - *web UI for viewing logs* +- Fluent Bit - *collects logs from Docker and forwards to OpenSearch* + +### Start Gateway without Logging Services + +To run only the core gateway services without the demonstration logging stack: + +```bash +docker compose up +``` + +**Note:** The gateway components still log to stdout/stderr. You just won't have the centralized collection and visualization services running. You can still view logs using: + +```bash +docker logs gateway-controller +docker logs policy-engine +docker logs router +``` + +### Stop Logging Services + +To stop all services including the logging stack: + +```bash +docker compose --profile logging down +``` + +To completely remove logging data: + +```bash +docker compose --profile logging down -v +``` + +This removes the `opensearch-data` volume containing all stored logs. + +## Viewing Logs in OpenSearch Dashboards + +Once you've started the gateway with the logging profile, follow these steps to view component logs: + +### Step 1: Access OpenSearch Dashboards + +Open your browser and navigate to: +``` +http://localhost:5601 +``` + +### Step 2: Create an Index Pattern + +Before you can view logs, you need to create an index pattern: + +1. Click on the **hamburger menu (☰)** in the top-left corner +2. Navigate to **Management** → **Dashboard Management** +3. Under **Dashboard Management**, click **Index Patterns** +4. Click **Create index pattern** +5. Enter the index pattern: `gateway-logs-*` +6. Click **Next step** +7. Select **@timestamp** as the time field +8. Click **Create index pattern** + +### Step 3: Navigate to Discover + +To view and explore logs: + +1. Click the **hamburger menu (☰)** +2. Navigate to **OpenSearch Dashboards** → **Discover** +3. Select the `gateway-logs-*` index pattern from the dropdown in the top-left +4. Adjust the time range in the top-right corner if needed (default is last 15 minutes) + +### Step 4: Filter Logs by Component + +To view logs for a specific gateway component, use filters: + +#### View Policy Engine Logs + +1. Click **Add filter** (below the search bar) +2. **Field**: Select `component` +3. **Operator**: Select `is` +4. **Value**: Enter `policy-engine` +5. Click **Save** + +#### View Gateway Controller Logs + +1. Click **Add filter** +2. **Field**: `component` +3. **Operator**: `is` +4. **Value**: `gateway-controller` +5. Click **Save** + +#### View Router (Envoy) Logs + +1. Click **Add filter** +2. **Field**: `component` +3. **Operator**: `is` +4. **Value**: `router` +5. Click **Save** + +### Step 5: Search and Filter Logs + +You can refine your log search using: + +#### Free Text Search +Enter keywords in the search bar at the top: +``` +error +``` +``` +Weather-API +``` + +#### Filter by Log Level +1. Click **Add filter** +2. **Field**: `level` +3. **Operator**: `is` +4. **Value**: `error` (or `info`, `warn`, `debug`) + +#### Combine Multiple Filters +Add multiple filters to narrow down results. For example: +- Component: `policy-engine` +- Level: `error` +- Time range: Last 1 hour + +#### Example Search Queries + +Search for errors in the policy engine: +``` +component:policy-engine AND level:error +``` + +Search for specific API logs: +``` +component:gateway-controller AND message:*Weather-API* +``` + +Search for slow requests (if duration field exists): +``` +duration_ms:>1000 +``` + +### Step 6: Customize Log View + +- **Add/Remove Columns**: Click the **gear icon** next to the field list to select which fields to display +- **Sort**: Click column headers to sort by that field +- **Expand Logs**: Click the **>** arrow next to any log entry to see full details in JSON format +- **Save Search**: Click **Save** in the top menu to save your filters and queries for later use + +## Alternative Logging Stacks + +While the default setup uses OpenSearch and Fluent Bit, you can integrate with other logging platforms: + +### Elastic Stack (ELK) + +Replace OpenSearch with the Elastic Stack: + +```yaml +elasticsearch: + image: docker.elastic.co/elasticsearch/elasticsearch:8.11.0 + environment: + - discovery.type=single-node + - xpack.security.enabled=false + ports: + - "9200:9200" + networks: + - gateway-network + +kibana: + image: docker.elastic.co/kibana/kibana:8.11.0 + environment: + - ELASTICSEARCH_HOSTS=http://elasticsearch:9200 + ports: + - "5601:5601" + networks: + - gateway-network + depends_on: + - elasticsearch +``` + +Update Fluent Bit output: +```conf +[OUTPUT] + Name es + Match docker.* + Host elasticsearch + Port 9200 + Logstash_Format On + Logstash_Prefix gateway-logs +``` + +### Grafana Loki + +For a lightweight, Prometheus-inspired logging solution: + +```yaml +loki: + image: grafana/loki:latest + ports: + - "3100:3100" + command: -config.file=/etc/loki/local-config.yaml + networks: + - gateway-network + +promtail: + image: grafana/promtail:latest + volumes: + - /var/lib/docker/containers:/var/lib/docker/containers:ro + - ./observability/promtail/config.yaml:/etc/promtail/config.yaml:ro + command: -config.file=/etc/promtail/config.yaml + networks: + - gateway-network + +grafana: + image: grafana/grafana:latest + ports: + - "3000:3000" + networks: + - gateway-network + depends_on: + - loki +``` + +### Cloud-Native Solutions + +#### AWS CloudWatch + +Configure Fluent Bit to send logs to CloudWatch: + +```conf +[OUTPUT] + Name cloudwatch_logs + Match * + region us-east-1 + log_group_name /aws/gateway + log_stream_prefix gateway- + auto_create_group true +``` + +Add AWS credentials via environment variables or IAM roles. + +#### Datadog + +Use the Datadog Agent: + +```yaml +datadog: + image: datadog/agent:latest + environment: + - DD_API_KEY=${DD_API_KEY} + - DD_LOGS_ENABLED=true + - DD_LOGS_CONFIG_CONTAINER_COLLECT_ALL=true + - DD_AC_EXCLUDE=name:datadog-agent + volumes: + - /var/run/docker.sock:/var/run/docker.sock:ro + - /var/lib/docker/containers:/var/lib/docker/containers:ro + networks: + - gateway-network +``` + +#### Splunk + +Configure Fluent Bit to forward to Splunk HEC: + +```conf +[OUTPUT] + Name splunk + Match * + Host splunk.example.com + Port 8088 + Splunk_Token ${SPLUNK_HEC_TOKEN} + TLS On + TLS.Verify Off +``` + + +## Additional Resources + +- [Fluent Bit Documentation](https://docs.fluentbit.io/) +- [OpenSearch Documentation](https://opensearch.org/docs/latest/) +- [OpenSearch Dashboards User Guide](https://opensearch.org/docs/latest/dashboards/) +- [Gateway Overview](../overview.md) +- [Docker Compose Profiles](https://docs.docker.com/compose/profiles/) diff --git a/en/docs/api-gateway/1.2.0/observability/metrics/alternative-backends.md b/en/docs/api-gateway/1.2.0/observability/metrics/alternative-backends.md new file mode 100644 index 000000000..2fe3b4d4c --- /dev/null +++ b/en/docs/api-gateway/1.2.0/observability/metrics/alternative-backends.md @@ -0,0 +1,186 @@ +--- +title: "Export Metrics to Alternative Backends" +description: "Export API Platform Gateway metrics to alternative backends including AWS CloudWatch, Datadog, and other Prometheus-compatible systems." +canonical_url: https://wso2.com/api-platform/docs/api-gateway/observability/metrics/alternative-backends/ +md_url: https://wso2.com/api-platform/docs/api-gateway/observability/metrics/alternative-backends.md +tags: + - api-gateway + - observability + - metrics +author: WSO2 API Platform Documentation Team +last_updated: 2026-06-17 +content_type: "how-to" +--- + +# Alternative Metrics Backends + +While the default setup uses Prometheus and Grafana, the gateway components expose standard Prometheus metrics and can integrate with any Prometheus-compatible system. + +## AWS CloudWatch + +Use AWS Distro for OpenTelemetry (ADOT) to export metrics to CloudWatch: + +```yaml +adot-collector: + image: public.ecr.aws/aws-observability/aws-otel-collector:latest + command: ["--config=/etc/otel-collector-config.yaml"] + environment: + - AWS_REGION=us-east-1 +``` + +Configure ADOT collector to scrape Prometheus metrics and export to CloudWatch. + +## Datadog + +Use Datadog Agent to scrape Prometheus metrics: + +```yaml +datadog-agent: + image: datadog/agent:latest + environment: + - DD_API_KEY=${DD_API_KEY} + - DD_METRICS_SCRAPER_ENABLED=true + - DD_SCRAPE_SERVICE_CHECKS=true + volumes: + - /var/run/docker.sock:/var/run/docker.sock:ro + networks: + - gateway-network +``` + +Configure Datadog to scrape gateway endpoints: +```yaml +instances: + - prometheus_url: http://gateway-controller:9091/metrics + namespace: gateway + metrics: + - gateway_controller_* + - prometheus_url: http://policy-engine:9003/metrics + namespace: policy_engine + metrics: + - policy_engine_* +``` + +## New Relic + +Use New Relic's Prometheus remote write integration: + +```yaml +prometheus: + image: prom/prometheus:latest + command: + - '--config.file=/etc/prometheus/prometheus.yml' + - '--remote.write.url=https://metric-api.newrelic.com/prometheus/v1/write?account_id=YOUR_ACCOUNT_ID' + - '--remote.write.headers=X-Api-Key:YOUR_API_KEY' +``` + +## InfluxDB + +Use Prometheus remote write to send metrics to InfluxDB: + +```yaml +prometheus: + image: prom/prometheus:latest + command: + - '--config.file=/etc/prometheus/prometheus.yml' + - '--remote.write.url=http://influxdb:8086/api/v1/prom/write?db=prometheus' +``` + +## Elasticsearch + +Use Metricbeat to ship Prometheus metrics to Elasticsearch: + +```yaml +metricbeat: + image: elastic/metricbeat:latest + volumes: + - ./metricbeat.yml:/usr/share/metricbeat/metricbeat.yml:ro + environment: + - ELASTICSEARCH_HOST=elasticsearch:9200 +``` + +Configure Metricbeat to scrape Prometheus endpoints. + +## Azure Monitor + +Use Azure Monitor Agent with Prometheus scraping: + +```yaml +azuremonitor-agent: + image: mcr.microsoft.com/azuremonitor/metrics-adapter:latest + environment: + - AZURE_CLIENT_ID=${AZURE_CLIENT_ID} + - AZURE_TENANT_ID=${AZURE_TENANT_ID} + - AZURE_CLIENT_SECRET=${AZURE_CLIENT_SECRET} +``` + +## Google Cloud Monitoring + +Use Cloud Monitoring Prometheus sidecar: + +```yaml +prometheus-to-monitoring: + image: gcr.io/cloud-prometheus/prometheus-to-monitoring:latest + environment: + - GOOGLE_APPLICATION_CREDENTIALS=/var/secrets/google/key.json + volumes: + - ./key.json:/var/secrets/google/key.json:ro +``` + +## Grafana Cloud + +Use Prometheus remote write to Grafana Cloud: + +```yaml +prometheus: + image: prom/prometheus:latest + command: + - '--config.file=/etc/prometheus/prometheus.yml' + - '--remote.write.url=https://YOUR-PROMETHEUS-URL/api/v1/write' + - '--remote.write.headers=Authorization:Bearer YOUR-API-KEY' +``` + +## VictoriaMetrics + +VictoriaMetrics is a Prometheus-compatible time-series database: + +```yaml +victoriametrics: + image: victoriametrics/victoria-metrics:latest + ports: + - "8428:8428" + volumes: + - victoriametrics-data:/victoria-metrics-data +``` + +Configure Prometheus to remote write to VictoriaMetrics: +```yaml +prometheus: + image: prom/prometheus:latest + command: + - '--config.file=/etc/prometheus/prometheus.yml' + - '--remote.write.url=http://victoriametrics:8428/api/v1/write' +``` + +## Thanos + +Thanos provides long-term storage and global query view for Prometheus: + +```yaml +thanos-store: + image: thanosio/thanos:latest + ports: + - "10901:10901" + volumes: + - prometheus-data:/prometheus + +thanos-query: + image: thanosio/thanos:latest + ports: + - "10902:10902" + command: + - 'query' + - '--store=thanos-store:10901' +``` + +Configure Prometheus to upload blocks to Thanos object storage. + diff --git a/en/docs/api-gateway/1.2.0/observability/metrics/best-practices-and-troubleshooting.md b/en/docs/api-gateway/1.2.0/observability/metrics/best-practices-and-troubleshooting.md new file mode 100644 index 000000000..ceba9c859 --- /dev/null +++ b/en/docs/api-gateway/1.2.0/observability/metrics/best-practices-and-troubleshooting.md @@ -0,0 +1,326 @@ +--- +title: "Metrics Best Practices and Troubleshooting" +description: "Best practices for correlating API Platform Gateway metrics with logs and traces, and guidance for troubleshooting common metrics issues." +canonical_url: https://wso2.com/api-platform/docs/api-gateway/observability/metrics/best-practices-and-troubleshooting/ +md_url: https://wso2.com/api-platform/docs/api-gateway/observability/metrics/best-practices-and-troubleshooting.md +tags: + - api-gateway + - observability + - metrics + - troubleshooting +author: WSO2 API Platform Documentation Team +last_updated: 2026-06-17 +content_type: "how-to" +--- + +# Best Practices and Troubleshooting + +## Integration with Logging and Tracing + +Metrics, logs, and traces work together for comprehensive observability: + +### Correlating Metrics with Logs + +1. **Correlation IDs**: Gateway components include request IDs in both logs and metrics +2. **Find Logs from Metrics**: Use metric labels to filter logs (e.g., API name, route) +3. **Find Metrics from Logs**: Copy request IDs from logs and query metrics with labels + +Example: If you see errors in logs for a specific API, query metrics for that API: +```promql +gateway_controller_api_operations_total{api_name="Weather-API", status="failure"} +``` + +### Correlating Metrics with Traces + +Metrics and traces share labels for correlation: +- Trace IDs are included in log entries +- You can search traces by API name or route +- Span attributes include metric labels + +### Using All Three Stacks + +Enable all observability profiles: +```bash +docker compose --profile logging --profile tracing --profile metrics up -d +``` + +This provides: +- **Metrics**: Quantitative measurements and alerting +- **Traces**: Request flow and performance debugging +- **Logs**: Detailed event information and error context + +## Best Practices + +### Development +- Use default scrape interval (15s) for reasonable granularity +- Keep short retention (7-15 days) to save disk space +- Enable debug logging for troubleshooting +- Use Grafana dashboards for real-time monitoring + +### Production +- **Adjust scrape intervals** based on traffic: + - Low traffic (<100 req/s): 15s interval + - Medium traffic (100-1000 req/s): 10s interval + - High traffic (>1000 req/s): 5s interval +- **Configure appropriate retention**: + - Short-term (hot): 7-30 days + - Medium-term (warm): 90 days + - Long-term (cold): 1+ years (use Thanos or remote write) +- **Set up alerts** for critical metrics: + - Error rate > 5% + - 95th percentile latency > 1s + - Memory usage > 80% + - Active streams approaching limit +- **Use recording rules** for frequently queried metrics: + ```yaml + groups: + - name: recording_rules + interval: 30s + rules: + - record: gateway:api_error_rate_5m + expr: | + rate(gateway_controller_api_operations_total{status="failure"}[5m]) / + rate(gateway_controller_api_operations_total[5m]) + ``` +- **Monitor Prometheus itself**: + - Scrape duration + - Rule evaluation time + - Storage usage + - Query performance + +### Security +- **Restrict metrics endpoints** in production +- **Enable authentication** for Grafana +- **Use TLS** for metrics endpoints (if exposed externally) +- **Sanitize sensitive data** from metrics +- **Implement access controls** for dashboards +- **Regularly audit** dashboard and alert permissions + +### Performance +- **Optimize PromQL queries**: + - Use rate() for counters over time ranges + - Use histogram_quantile() for percentiles + - Avoid high-cardinality labels (e.g., user IDs) +- **Use recording rules** for expensive queries +- **Limit dashboard refresh rates** (30s minimum) +- **Prune unused metrics** to reduce cardinality +- **Compress metric names** to reduce storage + +### Metric Cardinality + +Avoid high-cardinality labels (millions of unique values): + +**Good** (low cardinality): +```promql +gateway_controller_api_operations_total{route="/weather/v1"} +``` + +**Bad** (high cardinality): +```promql +gateway_controller_api_operations_total{user_id="12345"} +``` + +### Query Optimization + +**Use time ranges**: +```promql +# Bad: No time range (prometheus returns default) +gateway_controller_api_operations_total + +# Good: Explicit rate over 5 minutes +rate(gateway_controller_api_operations_total[5m]) +``` + +**Use subqueries efficiently**: +```promql +# Bad: Outer query has range, inner query has range +rate(rate(gateway_controller_http_request_duration_seconds_sum[5m])[10m:1m]) + +# Good: Single rate call +rate(gateway_controller_http_request_duration_seconds_sum[5m]) +``` + +## Troubleshooting + +### Metrics Not Appearing in Grafana + +**1. Verify metrics are enabled in configuration:** +```bash +grep -A5 "metrics" gateway/configs/config.toml +``` + +Ensure `enabled = true`. + +**2. Check Prometheus is running:** +```bash +docker ps | grep prometheus +curl http://localhost:9092/-/healthy +``` + +**3. Verify Prometheus configuration:** +```bash +docker exec prometheus cat /etc/prometheus/prometheus.yml +``` + +**4. Check Prometheus targets:** +- Navigate to +- Verify all endpoints are "UP" (green) +- If endpoints are "DOWN", check: + - Container is running + - Port is accessible from Prometheus container + - Metrics endpoint is responding + +**5. Test metrics endpoint directly:** +```bash +curl http://localhost:9091/metrics | head -20 # Gateway Controller +curl http://localhost:9003/metrics | head -20 # Policy Engine +``` + +**6. Check Grafana data source:** +- Navigate to +- Verify Prometheus data source is configured +- Test connection should succeed + +**7. Verify network connectivity:** +```bash +docker exec prometheus wget -O- gateway-controller:9091/metrics +docker exec prometheus wget -O- policy-engine:9003/metrics +``` + +### High Cardinality Metrics + +**Symptoms:** +- Prometheus memory usage constantly increasing +- Slow query performance +- Many unique label value combinations + +**Diagnosis:** +```bash +# Check metric cardinality +curl http://localhost:9091/metrics | wc -l # Count metric lines +``` + +**Solutions:** +- Remove high-cardinality labels (user IDs, session IDs, etc.) +- Use histogram buckets instead of labels +- Aggregate before labeling + +### Missing Metrics + +**1. Check if metric name changed** (after component update) +```bash +curl http://localhost:9091/metrics | grep "gateway_controller_" | head -20 +``` + +**2. Verify metrics are being scraped** +```promql +# Prometheus query to check if metric exists +up{job="gateway-controller"} +``` + +**3. Check component logs for metrics errors:** +```bash +docker logs gateway-controller | grep -i metric +docker logs policy-engine | grep -i metric +``` + +### Grafana Dashboards Not Loading + +**1. Verify Grafana is running:** +```bash +docker ps | grep grafana +curl http://localhost:3000/api/health +``` + +**2. Check Grafana logs:** +```bash +docker logs grafana +``` + +**3. Verify data source configuration:** +- Navigate to +- Check Prometheus URL: `http://prometheus:9090` +- Test connection + +**4. Clear browser cache and reload dashboard** + +**5. Re-import dashboards:** +```bash +# Navigate to Dashboards → Import +# Upload JSON files from ./observability/grafana/dashboards/ +``` + +### High Memory Usage + +**1. Check Prometheus memory usage:** +```bash +docker stats prometheus +``` + +**2. Review retention settings:** +```bash +docker exec prometheus cat /etc/prometheus/prometheus.yml | grep retention +``` + +**3. Check metric cardinality:** +```promql +# Count unique metric label combinations +count by (__name__) ({__name__=~".+"}) +``` + +**4. Reduce retention:** +```yaml +command: + - '--storage.tsdb.retention.time=7d' + - '--storage.tsdb.retention.size=2GB' +``` + +**5. Use Thanos or remote write** for long-term storage + +### Slow Queries + +**1. Identify slow queries:** +```promql +# Check query duration in Prometheus UI +# Navigate to http://localhost:9092/graph +# Run query and check execution time +``` + +**2. Optimize queries:** +- Use rate() instead of raw counters +- Use proper time ranges +- Avoid high-cardinality labels +- Use recording rules for common queries + +**3. Increase Prometheus resources:** +```yaml +prometheus: + image: prom/prometheus:latest + deploy: + resources: + limits: + memory: 4G + cpus: '2' +``` + +### Metric Values Not Updating + +**1. Check if metrics are counters with rate:** +```promql +# Counter without rate (shows cumulative total) +gateway_controller_api_operations_total + +# Counter with rate (shows rate of change) +rate(gateway_controller_api_operations_total[5m]) +``` + +**2. Verify scrape configuration:** +```bash +docker exec prometheus cat /etc/prometheus/prometheus.yml | grep -A5 job_name +``` + +**3. Check component is receiving traffic:** +```promql +rate(gateway_controller_api_operations_total[5m]) > 0 +``` + diff --git a/en/docs/api-gateway/1.2.0/observability/metrics/configuration.md b/en/docs/api-gateway/1.2.0/observability/metrics/configuration.md new file mode 100644 index 000000000..ee5520a80 --- /dev/null +++ b/en/docs/api-gateway/1.2.0/observability/metrics/configuration.md @@ -0,0 +1,73 @@ +--- +title: "Metrics Configuration Options" +description: "Configure Prometheus scrape intervals and custom metrics endpoints for API Platform Gateway components." +canonical_url: https://wso2.com/api-platform/docs/api-gateway/observability/metrics/configuration/ +md_url: https://wso2.com/api-platform/docs/api-gateway/observability/metrics/configuration.md +tags: + - api-gateway + - observability + - metrics +author: WSO2 API Platform Documentation Team +last_updated: 2026-06-17 +content_type: "reference" +--- + +# Configuration Options + +## Adjusting Scrape Interval + +To reduce metrics collection overhead or increase granularity, adjust the Prometheus scrape interval: + +Edit `gateway/observability/prometheus/prometheus.yml`: + +```yaml +global: + scrape_interval: 15s # Change to 5s for higher granularity, 60s for lower overhead + evaluation_interval: 15s +``` + +Restart Prometheus after changes: +```bash +docker compose restart prometheus +``` + +## Custom Metrics Endpoints + +You can add additional metrics endpoints to scrape from: + +```yaml +scrape_configs: + - job_name: 'custom-service' + static_configs: + - targets: ['custom-service:8080'] + metrics_path: /metrics + scrape_interval: 30s +``` + +## Metric Retention + +Configure how long Prometheus retains metrics: + +```yaml +command: + - '--config.file=/etc/prometheus/prometheus.yml' + - '--storage.tsdb.path=/prometheus' + - '--web.enable-lifecycle' + - '--storage.tsdb.retention.time=30d' # Keep metrics for 30 days + - '--storage.tsdb.retention.size=10GB' # Keep up to 10GB of metrics +``` + +## Custom Bucket Configuration + +Gateway components use optimized bucket configurations for histograms: + +**Request Duration Buckets**: +- Gateway Controller: `[0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1.0, 2.5, 5.0]` +- Policy Engine: `[0.001, 0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1.0, 2.5]` + +**Policy Execution Buckets**: +- `[0.0001, 0.0005, 0.001, 0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5]` + +**Deployment Latency Buckets**: +- `[0.1, 0.5, 1.0, 2.5, 5.0, 10.0, 30.0, 60.0]` + diff --git a/en/docs/api-gateway/1.2.0/observability/metrics/enabling-metrics.md b/en/docs/api-gateway/1.2.0/observability/metrics/enabling-metrics.md new file mode 100644 index 000000000..b11978574 --- /dev/null +++ b/en/docs/api-gateway/1.2.0/observability/metrics/enabling-metrics.md @@ -0,0 +1,138 @@ +--- +title: "Enable Metrics" +description: "Enable and configure Prometheus metrics collection for API Platform Gateway components in config.toml." +canonical_url: https://wso2.com/api-platform/docs/api-gateway/observability/metrics/enabling-metrics/ +md_url: https://wso2.com/api-platform/docs/api-gateway/observability/metrics/enabling-metrics.md +tags: + - api-gateway + - observability + - metrics + - prometheus +author: WSO2 API Platform Documentation Team +last_updated: 2026-06-17 +content_type: "how-to" +--- + +# Enabling and Disabling Metrics + +## Enabling Metrics + +### Configuration Required + +You need to enable metrics in the gateway configuration file. By default, metrics are enabled in the production configuration but you can customize the settings. + +The metrics configuration is located in `gateway/configs/config.toml`: + +#### Gateway Controller Metrics Configuration + +```toml +[controller.metrics] +# Enable or disable Prometheus metrics endpoint +enabled = true + +# Port for metrics HTTP server +port = 9091 +``` + +#### Policy Engine Metrics Configuration + +```toml +[policy_engine.metrics] +# Enable or disable Prometheus metrics endpoint +enabled = true + +# Port for metrics HTTP server +port = 9003 +``` + +**Note**: When metrics are enabled, each component starts an HTTP server on the specified port to expose metrics in Prometheus format. + +### Demonstrated Metrics Services + +The metrics services included in the Docker Compose file (Prometheus and Grafana) are provided as **demonstration services** to show one possible way to collect and visualize metrics. You can use them as-is for development/testing, or replace them with your own metrics solution. + +The gateway uses Docker Compose profiles to optionally enable these demonstration metrics services. + +#### Setting up Grafana Image + +**Important Note:** The Grafana image in the `docker-compose.yaml` file is intentionally left empty due to licensing considerations. Before you can use the demonstration dashboards, you must specify a valid Grafana image. + +To add the Grafana image: + +1. Open `gateway/docker-compose.yaml`. +2. Locate the `grafana` service definition. +3. Update the `image` field with a valid Grafana image tag (e.g., `grafana/grafana:11.6.0` or another compatible version). + +```yaml + grafana: + image: grafana/grafana:11.6.0 # Add your preferred Grafana image here + container_name: grafana +``` + +### Start Gateway with Demonstrated Metrics Services + +To start the gateway with the demonstration metrics services enabled: + +```bash +docker compose --profile metrics up -d +``` + +This starts: +- Core gateway services (gateway-controller, policy-engine, router) - *which expose metrics on their respective ports* +- Prometheus - *scrapes and stores metrics* +- Grafana - *visualizes metrics through dashboards* + +### Start Gateway without Metrics Services + +To run only the core gateway services without the demonstration metrics stack: + +```bash +docker compose up +``` + +**Note:** The gateway components still expose metrics if enabled in the configuration. You can still access metrics directly at: +- Gateway Controller: +- Policy Engine: +- Router (Envoy): + +### Stop Metrics Services + +To stop all services including the metrics stack: + +```bash +docker compose --profile metrics down +``` + +To completely remove metrics data: + +```bash +docker compose --profile metrics down -v +``` + +This removes the `prometheus-data` volume containing all stored metrics. + +## Disabling Metrics + +To completely disable metrics: + +1. **Update configuration** in `gateway/configs/config.toml`: + +```toml +[controller.metrics] +enabled = false + +[policy_engine.metrics] +enabled = false +``` + +2. **Restart gateway services:** + +```bash +docker compose restart gateway-controller policy-engine router +``` + +**Note:** Disabling metrics will: +- Stop HTTP metrics servers on the configured ports +- Remove metrics from Prometheus targets +- No new metric data will be collected + diff --git a/en/docs/api-gateway/1.2.0/observability/metrics/metric-reference.md b/en/docs/api-gateway/1.2.0/observability/metrics/metric-reference.md new file mode 100644 index 000000000..7bc27b74e --- /dev/null +++ b/en/docs/api-gateway/1.2.0/observability/metrics/metric-reference.md @@ -0,0 +1,182 @@ +--- +title: "Metric Reference" +description: "Complete reference for all Prometheus metrics exposed by API Platform Gateway controller, router, and policy engine components." +canonical_url: https://wso2.com/api-platform/docs/api-gateway/observability/metrics/metric-reference/ +md_url: https://wso2.com/api-platform/docs/api-gateway/observability/metrics/metric-reference.md +tags: + - api-gateway + - observability + - metrics +author: WSO2 API Platform Documentation Team +last_updated: 2026-06-17 +content_type: "reference" +--- + +# Metric Reference + +## Gateway Controller Metrics + +### API Management +- `gateway_controller_api_operations_total`: Counter of API operations + - Labels: `operation`, `status`, `api_type` +- `gateway_controller_api_operation_duration_seconds`: Histogram of operation duration + - Labels: `operation`, `api_type` +- `gateway_controller_apis_total`: Gauge of deployed APIs + - Labels: `api_type`, `status` +- `gateway_controller_validation_errors_total`: Counter of validation errors + - Labels: `operation`, `error_type` +- `gateway_controller_deployment_latency_seconds`: Histogram of deployment latency + +### xDS Metrics +- `gateway_controller_xds_clients_connected`: Gauge of connected xDS clients + - Labels: `server`, `node_id` +- `gateway_controller_snapshot_generation_duration_seconds`: Histogram of snapshot generation time + - Labels: `type` +- `gateway_controller_snapshot_generation_total`: Counter of snapshot generations + - Labels: `type`, `status`, `trigger` +- `gateway_controller_snapshot_size`: Gauge of snapshot resource size + - Labels: `resource_type` +- `gateway_controller_xds_stream_requests_total`: Counter of xDS stream requests + - Labels: `server`, `type_url`, `operation` +- `gateway_controller_xds_snapshot_ack_total`: Counter of snapshot ACK/NACK + - Labels: `server`, `node_id`, `status` + +### Database Metrics +- `gateway_controller_database_operations_total`: Counter of database operations + - Labels: `operation`, `table`, `status` +- `gateway_controller_database_operation_duration_seconds`: Histogram of operation duration + - Labels: `operation`, `table` +- `gateway_controller_database_size_bytes`: Gauge of database size + - Labels: `database` +- `gateway_controller_config_store_size`: Gauge of config store items + - Labels: `type` + +### HTTP API Metrics +- `gateway_controller_http_requests_total`: Counter of HTTP requests + - Labels: `method`, `endpoint`, `status_code` +- `gateway_controller_http_request_duration_seconds`: Histogram of request duration + - Labels: `method`, `endpoint` +- `gateway_controller_http_request_size_bytes`: Histogram of request size + - Labels: `endpoint` +- `gateway_controller_http_response_size_bytes`: Histogram of response size + - Labels: `endpoint` +- `gateway_controller_concurrent_requests`: Gauge of concurrent requests + +### System Metrics +- `gateway_controller_up`: Gauge of component liveness (1=up, 0=down) +- `gateway_controller_info`: Gauge of build information + - Labels: `version`, `storage_type`, `build_date` +- `gateway_controller_goroutines`: Gauge of current goroutines +- `gateway_controller_memory_bytes`: Gauge of memory usage + - Labels: `type` (heap_alloc, heap_sys, stack_inuse) + +### Error Metrics +- `gateway_controller_errors_total`: Counter of errors + - Labels: `component`, `error_type` +- `gateway_controller_panic_recoveries_total`: Counter of panic recoveries + - Labels: `component` +- `gateway_controller_storage_errors_total`: Counter of storage errors + - Labels: `operation`, `error_type` +- `gateway_controller_translation_errors_total`: Counter of translation errors + - Labels: `error_type` + +### Certificate Metrics +- `gateway_controller_certificates_total`: Gauge of certificates + - Labels: `type` +- `gateway_controller_certificate_operations_total`: Counter of certificate operations + - Labels: `operation`, `status` +- `gateway_controller_certificate_expiry_seconds`: Gauge of certificate expiry + - Labels: `cert_id`, `cert_name` + +### Policy Metrics +- `gateway_controller_policies_total`: Gauge of policies + - Labels: `api_id`, `route` +- `gateway_controller_policy_chain_length`: Histogram of policy chain length + - Labels: `api_id`, `route` +- `gateway_controller_policy_snapshot_updates_total`: Counter of policy updates + - Labels: `status` +- `gateway_controller_policy_validation_errors_total`: Counter of validation errors + - Labels: `error_type` + +## Policy Engine Metrics + +### Request Processing +- `policy_engine_requests_total`: Counter of processed requests + - Labels: `phase` (request, response), `route`, `api_name`, `api_version` +- `policy_engine_request_duration_seconds`: Histogram of request duration + - Labels: `phase`, `route` +- `policy_engine_request_errors_total`: Counter of request errors + - Labels: `phase`, `error_type`, `route` +- `policy_engine_short_circuits_total`: Counter of short-circuited requests + - Labels: `route`, `policy_name` + +### Policy Execution +- `policy_engine_policy_executions_total`: Counter of policy executions + - Labels: `policy_name`, `policy_version`, `api`, `route`, `status` +- `policy_engine_policy_duration_seconds`: Histogram of policy execution duration + - Labels: `policy_name`, `policy_version`, `api`, `route` +- `policy_engine_policy_skipped_total`: Counter of skipped policies + - Labels: `policy_name`, `api`, `route`, `reason` +- `policy_engine_policies_per_chain`: Gauge of current policies per chain + - Labels: `route`, `api` + +### Configuration +- `policy_engine_policy_chains_loaded`: Gauge of loaded policy chains + - Labels: `mode` (file, xds) +- `policy_engine_xds_updates_total`: Counter of xDS updates + - Labels: `status`, `type` +- `policy_engine_xds_connection_state`: Gauge of xDS connection state + - Labels: `state` +- `policy_engine_snapshot_size`: Gauge of snapshot size + - Labels: `resource_type` + +### Streaming +- `policy_engine_active_streams`: Gauge of active ext_proc streams +- `policy_engine_body_bytes_processed`: Counter of body bytes processed + - Labels: `phase`, `operation` +- `policy_engine_context_build_duration_seconds`: Histogram of context build duration + - Labels: `type` +- `policy_engine_grpc_connections_active`: Gauge of active gRPC connections + - Labels: `type` + +### System Metrics +- `policy_engine_up`: Gauge of component liveness (1=up, 0=down) +- `policy_engine_goroutines`: Gauge of current goroutines +- `policy_engine_memory_bytes`: Gauge of memory usage + - Labels: `type` (heap_alloc, heap_sys, stack) + +### Error Metrics +- `policy_engine_policy_errors_total`: Counter of policy errors + - Labels: `policy_name`, `error_type` +- `policy_engine_stream_errors_total`: Counter of stream errors + - Labels: `error_type` +- `policy_engine_route_lookup_failures_total`: Counter of route lookup failures +- `policy_engine_panic_recoveries_total`: Counter of panic recoveries + - Labels: `component` + +## Router (Envoy) Metrics + +Envoy exposes built-in Prometheus metrics. Key metrics include: + +### HTTP Metrics +- `envoy_http_internal_requests_total`: Counter of HTTP requests + - Labels: `virtual_cluster`, `virtual_host`, `response_code` +- `envoy_http_request_duration_seconds`: Histogram of request duration +- `envoy_http_downstream_cx_active`: Gauge of active connections +- `envoy_http_downstream_cx_total`: Counter of connections + +### Upstream Metrics +- `envoy_http_upstream_rq_total`: Counter of upstream requests + - Labels: `upstream_cluster`, `response_code` +- `envoy_http_upstream_rq_xx`: Counter of upstream requests by status + - Labels: `upstream_cluster`, `envoy_response_flags` + +### Cluster Metrics +- `envoy_cluster_upstream_cx_active`: Gauge of active upstream connections +- `envoy_cluster_upstream_rq_retry_total`: Counter of retry requests +- `envoy_cluster_membership_healthy`: Gauge of healthy endpoints + +### Listener Metrics +- `envoy_listener_downstream_cx_active`: Gauge of active downstream connections +- `envoy_listener_downstream_cx_total`: Counter of downstream connections + diff --git a/en/docs/api-gateway/1.2.0/observability/metrics/overview.md b/en/docs/api-gateway/1.2.0/observability/metrics/overview.md new file mode 100644 index 000000000..2c30ef469 --- /dev/null +++ b/en/docs/api-gateway/1.2.0/observability/metrics/overview.md @@ -0,0 +1,68 @@ +--- +title: "Gateway Metrics" +description: "Configure metrics collection for API Platform Gateway components using Prometheus and Grafana, with reference dashboards and alerting." +canonical_url: https://wso2.com/api-platform/docs/api-gateway/observability/metrics/ +md_url: https://wso2.com/api-platform/docs/api-gateway/observability/metrics/overview.md +tags: + - api-gateway + - observability + - devops +author: WSO2 API Platform Documentation Team +last_updated: 2026-06-11 +content_type: "how-to" +--- + +# Gateway Metrics + +This guide explains how to implement and configure metrics collection for the API Platform Gateway components. + +## Overview + +The default metrics services included in the Docker Compose configuration are **demonstration services** designed to showcase how you can observe component metrics in a centralized setup. These services provide a reference implementation that you can use out-of-the-box for development, testing, or as a starting point for your production metrics strategy. + +**Important**: You are free to choose any metrics or observability strategy that suits your environment and requirements. The provided setup is just one of many possible configurations. + +## Metrics Architecture + +The default metrics stack consists of: + +- **Prometheus**: Scrapes, stores, and queries metrics from gateway components +- **Grafana**: Visualizes metrics through dashboards with alerts and notifications + +### How It Works + +1. Gateway components (gateway-controller, policy-engine, router) expose metrics via Prometheus HTTP endpoints +2. Prometheus scrapes these endpoints at regular intervals (default: 15s) +3. Metrics are stored in Prometheus's time-series database +4. Grafana queries Prometheus to visualize metrics through pre-built dashboards +5. Users can view real-time metrics, historical trends, and set up alerts + +### What are Metrics? + +Metrics are numerical measurements tracked over time: + +- **Counters**: Cumulative values that only increase (e.g., total requests, total errors) +- **Gauges**: Current values that can go up or down (e.g., active connections, memory usage) +- **Histograms**: Sample observations with configurable buckets (e.g., request duration) + +## In This Section + +- [Enabling and Disabling Metrics](enabling-metrics.md) — Turn metrics on or off and run the demonstration metrics services +- [Viewing Metrics in Grafana](viewing-metrics-in-grafana.md) — Explore pre-built dashboards, create custom dashboards, and set up alerts +- [Prometheus Queries](prometheus-queries.md) — Useful PromQL queries for gateway controller, policy engine, and router +- [Configuration Options](configuration.md) — Scrape interval, custom endpoints, retention, and histogram buckets +- [Alternative Metrics Backends](alternative-backends.md) — Export metrics to CloudWatch, Datadog, New Relic, and other systems +- [Metric Reference](metric-reference.md) — Full list of metrics exposed by each gateway component +- [Best Practices and Troubleshooting](best-practices-and-troubleshooting.md) — Recommendations, common issues, and integration with logging and tracing + +## Additional Resources + +- [Prometheus Documentation](https://prometheus.io/docs/) +- [Grafana Documentation](https://grafana.com/docs/) +- [Prometheus Querying Guide](https://prometheus.io/docs/prometheus/latest/querying/basics/) +- [Grafana Dashboard Tutorial](https://grafana.com/docs/grafana/latest/dashboards/) +- [Prometheus Best Practices](https://prometheus.io/docs/practices/naming/) +- [Gateway Overview](../../overview.md) +- [Gateway Logging Guide](../logging.md) +- [Gateway Tracing Guide](../tracing/overview.md) +- [Docker Compose Profiles](https://docs.docker.com/compose/profiles/) diff --git a/en/docs/api-gateway/1.2.0/observability/metrics/prometheus-queries.md b/en/docs/api-gateway/1.2.0/observability/metrics/prometheus-queries.md new file mode 100644 index 000000000..128d8217c --- /dev/null +++ b/en/docs/api-gateway/1.2.0/observability/metrics/prometheus-queries.md @@ -0,0 +1,109 @@ +--- +title: "Prometheus Queries" +description: "Useful PromQL queries for monitoring API Platform Gateway controller, router, and policy engine metrics." +canonical_url: https://wso2.com/api-platform/docs/api-gateway/observability/metrics/prometheus-queries/ +md_url: https://wso2.com/api-platform/docs/api-gateway/observability/metrics/prometheus-queries.md +tags: + - api-gateway + - observability + - metrics + - prometheus +author: WSO2 API Platform Documentation Team +last_updated: 2026-06-17 +content_type: "reference" +--- + +# Prometheus Queries + +You can query Prometheus directly at to create custom visualizations or debug issues. + +## Useful Queries + +### Gateway Controller + +**Total API Operations**: +```promql +rate(gateway_controller_api_operations_total[5m]) +``` + +**API Operations by Status**: +```promql +rate(gateway_controller_api_operations_total[5m]) by (status) +``` + +**Deployment Latency Percentiles**: +```promql +histogram_quantile(0.95, rate(gateway_controller_deployment_latency_seconds_bucket[5m])) +``` + +**xDS Clients Connected**: +```promql +gateway_controller_xds_clients_connected +``` + +**Database Operation Rate**: +```promql +rate(gateway_controller_database_operations_total[5m]) +``` + +**Memory Usage**: +```promql +gateway_controller_memory_bytes +``` + +**HTTP Request Duration**: +```promql +histogram_quantile(0.99, rate(gateway_controller_http_request_duration_seconds_bucket[5m])) +``` + +### Policy Engine + +**Request Rate**: +```promql +rate(policy_engine_requests_total[5m]) +``` + +**Policy Execution Rate**: +```promql +rate(policy_engine_policy_executions_total[5m]) +``` + +**Policy Execution Success Rate**: +```promql +rate(policy_engine_policy_executions_total{status="success"}[5m]) / +rate(policy_engine_policy_executions_total[5m]) +``` + +**Average Request Duration**: +```promql +rate(policy_engine_request_duration_seconds_sum[5m]) / +rate(policy_engine_request_duration_seconds_count[5m]) +``` + +**Active Streams**: +```promql +policy_engine_active_streams +``` + +**Error Rate**: +```promql +rate(policy_engine_request_errors_total[5m]) +``` + +### Router (Envoy) + +**Request Rate**: +```promql +rate(envoy_http_internal_requests_total[5m]) +``` + +**Request Duration**: +```promql +histogram_quantile(0.99, rate(envoy_http_request_duration_seconds_bucket[5m])) +``` + +**Upstream 5xx Errors**: +```promql +rate(envoy_http_upstream_rq_xx{envoy_response_flags="upstream_connect_fail"}[5m]) +``` + diff --git a/en/docs/api-gateway/1.2.0/observability/metrics/viewing-metrics-in-grafana.md b/en/docs/api-gateway/1.2.0/observability/metrics/viewing-metrics-in-grafana.md new file mode 100644 index 000000000..3902a07e4 --- /dev/null +++ b/en/docs/api-gateway/1.2.0/observability/metrics/viewing-metrics-in-grafana.md @@ -0,0 +1,155 @@ +--- +title: "View Metrics in Grafana" +description: "Access pre-built Grafana dashboards to view and explore API Platform Gateway component metrics." +canonical_url: https://wso2.com/api-platform/docs/api-gateway/observability/metrics/viewing-metrics-in-grafana/ +md_url: https://wso2.com/api-platform/docs/api-gateway/observability/metrics/viewing-metrics-in-grafana.md +tags: + - api-gateway + - observability + - metrics + - grafana +author: WSO2 API Platform Documentation Team +last_updated: 2026-06-17 +content_type: "how-to" +--- + +# Viewing Metrics in Grafana + +Once you've started the gateway with the metrics profile, follow these steps to view component metrics: + +## Step 1: Access Grafana + +Open your browser and navigate to: + +## Step 2: Log in to Grafana + +1. Username: `admin` +2. Password: `admin` + +**Note**: You'll be prompted to change the password on first login. + +## Step 3: Navigate to Dashboards + +1. Click on the **hamburger menu (☰)** in the top-left corner +2. Navigate to **Dashboards** → **Browse** +3. You'll see several pre-built dashboards: + - **Infrastructure Overview**: High-level view of all components + - **Gateway Controller**: Detailed gateway-controller metrics + - **Policy Engine**: Detailed policy-engine metrics + +## Step 4: View Infrastructure Overview + +The Infrastructure Overview dashboard provides a comprehensive view: + +### Gateway Controller Section +- **API Operations**: Total operations and operation rate +- **Deployment Latency**: End-to-end deployment time +- **xDS Clients**: Number of connected Envoy routers +- **Database Operations**: Database operation metrics +- **HTTP Requests**: REST API request metrics + +### Policy Engine Section +- **Request Processing**: Total requests and request rate +- **Policy Executions**: Policy execution metrics +- **Active Streams**: Current ext_proc streams +- **Errors**: Error rate and types + +### System Resources +- **Memory Usage**: Heap, system memory across components +- **Goroutines**: Go runtime goroutines count +- **Uptime**: Component availability + +## Step 5: View Gateway Controller Dashboard + +The Gateway Controller dashboard provides detailed metrics: + +### API Management +- **API Operations Total**: Counter for all API operations with labels for: + - `operation`: create, update, delete, get + - `status`: success, failure + - `api_type`: REST, GraphQL, etc. +- **APIs Total**: Gauge showing deployed APIs by type and status +- **Deployment Latency Seconds**: Histogram of deployment times + +### xDS Metrics +- **xDS Clients Connected**: Gauge of connected Envoy instances +- **Snapshot Generation Duration**: Time to generate configuration snapshots +- **XDS Stream Requests**: Counter for xDS requests by type +- **Snapshot Size**: Size of generated configuration snapshots + +### Database Metrics +- **Database Operations Total**: Counter for database operations +- **Database Operation Duration**: Histogram of operation times +- **Database Size Bytes**: Current database size + +### HTTP API Metrics +- **HTTP Requests Total**: Counter for REST API requests +- **HTTP Request Duration**: Histogram of API response times +- **Concurrent Requests**: Current concurrent API requests + +## Step 6: View Policy Engine Dashboard + +The Policy Engine dashboard provides detailed metrics: + +### Request Processing +- **Requests Total**: Counter for all processed requests with labels: + - `phase`: request, response + - `route`: route name + - `api_name`: API identifier + - `api_version`: API version +- **Request Duration Seconds**: Histogram of request processing times +- **Request Errors Total**: Counter for errors by type + +### Policy Execution +- **Policy Executions Total**: Counter for policy executions with labels: + - `policy_name`: Name of executed policy + - `policy_version`: Policy version + - `api`: API identifier + - `route`: Route name + - `status`: success, failure, skip +- **Policy Duration Seconds**: Histogram of policy execution times +- **Policies Per Chain**: Gauge of current policy chain lengths + +### Streaming +- **Active Streams**: Current ext_proc streams (gauge) +- **XDS Updates Total**: Counter for configuration updates +- **Body Bytes Processed**: Counter for body processing + +### System Resources +- **Memory Usage**: Memory consumption metrics +- **Goroutines**: Current goroutines count +- **GRPC Connections**: Active gRPC connections + +## Step 7: Create Custom Dashboards + +You can create custom dashboards in Grafana: + +1. Click **+** → **Dashboard** +2. Click **+ Add visualization** +3. Select Prometheus as the data source +4. Write PromQL queries to fetch metrics +5. Configure visualization (graphs, tables, gauges, etc.) +6. Save the dashboard + +## Step 8: Set Up Alerts + +Create alerts to be notified of issues: + +1. Navigate to **Alerting** → **Alert rules** +2. Click **+ New alert rule** +3. Define the alert condition using PromQL +4. Set severity (Critical, Warning, Info) +5. Configure notifications (email, Slack, PagerDuty, etc.) +6. Save the alert rule + +Example alert for high error rate: +```promql +( + rate(gateway_controller_api_operations_total{status="failure"}[5m]) + / + rate(gateway_controller_api_operations_total[5m]) +) > 0.1 +``` + +This alert triggers when the error rate exceeds 10% over 5 minutes. + diff --git a/en/docs/api-gateway/1.2.0/observability/tracing/alternative-backends.md b/en/docs/api-gateway/1.2.0/observability/tracing/alternative-backends.md new file mode 100644 index 000000000..12e79eb64 --- /dev/null +++ b/en/docs/api-gateway/1.2.0/observability/tracing/alternative-backends.md @@ -0,0 +1,326 @@ +--- +title: "Export Traces to Alternative Backends" +description: "Configure API Platform Gateway to export OpenTelemetry traces to alternative backends including Moesif, Datadog, and New Relic." +canonical_url: https://wso2.com/api-platform/docs/api-gateway/observability/tracing/alternative-backends/ +md_url: https://wso2.com/api-platform/docs/api-gateway/observability/tracing/alternative-backends.md +tags: + - api-gateway + - observability + - tracing +author: WSO2 API Platform Documentation Team +last_updated: 2026-06-17 +content_type: "how-to" +--- + +# Alternative Tracing Backends + +While the default setup uses Jaeger, the gateway components use OpenTelemetry and can export to any OTLP-compatible backend. + +## Moesif + +Moesif provides API analytics and monitoring with support for OpenTelemetry traces. It treats each HTTP request/response span as an API event for detailed analytics. + +**No additional Docker services required** - Moesif is a cloud-based SaaS platform. You only need to configure the OTLP Collector to export traces to Moesif's API. + +### Configuration + +Update the OTLP Collector configuration (`gateway/observability/otel-collector/config.yaml`) to export to Moesif: + +```yaml +exporters: + # Export to Moesif + otlphttp: + endpoint: https://api.moesif.net/v1/traces + headers: + X-Moesif-Application-Id: 'your-moesif-application-id' + +service: + pipelines: + traces: + receivers: [otlp] + processors: [memory_limiter, batch, resource] + exporters: [otlphttp] # Send to Moesif +``` + +**Important Notes:** +- The endpoint uses HTTPS (not HTTP) +- Use the `otlphttp` exporter (not `otlp` which uses gRPC) +- The `X-Moesif-Application-Id` header is required for authentication + +### Obtaining Your Moesif Application ID + +1. Sign up for a Moesif account at [moesif.com](https://www.moesif.com) +2. Log in to your Moesif dashboard +3. Navigate to **Settings** → **Installation** or **API Keys** +4. Locate the **Collector Application ID** field +5. Copy your unique Application ID + +### Using Environment Variables + +For better security, use environment variables for the Application ID: + +```yaml +exporters: + otlphttp: + endpoint: https://api.moesif.net/v1/traces + headers: + X-Moesif-Application-Id: '${MOESIF_APPLICATION_ID}' +``` + +Update `docker-compose.yaml` to pass the environment variable: + +```yaml +otel-collector: + image: otel/opentelemetry-collector:latest + environment: + - MOESIF_APPLICATION_ID=${MOESIF_APPLICATION_ID} + # ... rest of configuration +``` + +Set the environment variable before starting: + +```bash +export MOESIF_APPLICATION_ID=your-moesif-application-id +docker compose --profile tracing up -d +``` + +### Accessing Moesif Dashboard + +After configuring and starting the gateway: + +1. Navigate to [moesif.com](https://www.moesif.com) and log in +2. Go to **Events** → **Live Event Log** to see incoming API events +3. View API analytics, user behavior, and performance metrics +4. Use **Time Series** to analyze API usage trends +5. Set up **Alerts** for error rates, latency, or custom conditions + +### Moesif Features + +- **API Analytics**: Request volume, response times, error rates +- **User Tracking**: Identify and track API users across requests +- **Error Analysis**: Detailed error tracking with request/response bodies +- **Behavioral Cohorts**: Group users by API usage patterns +- **Custom Dashboards**: Build visualizations for your specific KPIs +- **Alerting**: Get notified of anomalies or threshold breaches + +### Sending to Both Jaeger and Moesif + +You can send traces to both Jaeger (for development) and Moesif (for analytics): + +```yaml +exporters: + # Local Jaeger for development + otlp/jaeger: + endpoint: jaeger:4317 + tls: + insecure: true + + # Moesif for analytics + otlphttp/moesif: + endpoint: https://api.moesif.net/v1/traces + headers: + X-Moesif-Application-Id: '${MOESIF_APPLICATION_ID}' + +service: + pipelines: + traces: + receivers: [otlp] + processors: [memory_limiter, batch, resource] + exporters: [otlp/jaeger, otlphttp/moesif] +``` + +## Zipkin + +Replace Jaeger with Zipkin: + +```yaml +zipkin: + image: openzipkin/zipkin:latest + ports: + - "9411:9411" + networks: + - gateway-network +``` + +Update OTLP Collector configuration to export to Zipkin: + +```yaml +exporters: + zipkin: + endpoint: http://zipkin:9411/api/v2/spans +``` + +Access Zipkin UI at `http://localhost:9411` + +## Grafana Tempo + +For a Prometheus-style tracing backend: + +```yaml +tempo: + image: grafana/tempo:latest + command: ["-config.file=/etc/tempo.yaml"] + volumes: + - ./observability/tempo/tempo.yaml:/etc/tempo.yaml + - tempo-data:/tmp/tempo + ports: + - "3200:3200" # Tempo HTTP + - "4317:4317" # OTLP gRPC + networks: + - gateway-network + +grafana: + image: grafana/grafana:latest + environment: + - GF_AUTH_ANONYMOUS_ENABLED=true + - GF_AUTH_ANONYMOUS_ORG_ROLE=Admin + ports: + - "3000:3000" + volumes: + - ./observability/grafana/datasources.yaml:/etc/grafana/provisioning/datasources/datasources.yaml + networks: + - gateway-network + depends_on: + - tempo +``` + +Configure gateway to send directly to Tempo: +```yaml +tracing: + endpoint: tempo:4317 +``` + +## Cloud-Native Tracing Solutions + +### AWS X-Ray + +Configure OTLP Collector to export to AWS X-Ray: + +```yaml +exporters: + awsxray: + region: us-east-1 + no_verify_ssl: false +``` + +Or use the AWS Distro for OpenTelemetry (ADOT) Collector: + +```yaml +otel-collector: + image: public.ecr.aws/aws-observability/aws-otel-collector:latest + command: ["--config=/etc/otel-collector-config.yaml"] + environment: + - AWS_REGION=us-east-1 +``` + +### Google Cloud Trace + +Configure OTLP Collector to export to Google Cloud: + +```yaml +exporters: + googlecloud: + project: your-gcp-project-id + use_insecure: false +``` + +### Azure Monitor + +Use Azure Monitor exporter: + +```yaml +exporters: + azuremonitor: + instrumentation_key: "your-instrumentation-key" +``` + +### Datadog APM + +Configure OTLP Collector to export to Datadog: + +```yaml +exporters: + datadog: + api: + key: ${DD_API_KEY} + site: datadoghq.com +``` + +Or use Datadog Agent directly: + +```yaml +datadog-agent: + image: datadog/agent:latest + environment: + - DD_API_KEY=${DD_API_KEY} + - DD_APM_ENABLED=true + - DD_APM_NON_LOCAL_TRAFFIC=true + - DD_OTLP_CONFIG_RECEIVER_PROTOCOLS_GRPC_ENDPOINT=0.0.0.0:4317 + ports: + - "4317:4317" + networks: + - gateway-network +``` + +Update gateway configuration: +```yaml +tracing: + endpoint: datadog-agent:4317 +``` + +### New Relic + +Configure OTLP Collector to export to New Relic: + +```yaml +exporters: + otlphttp: + endpoint: https://otlp.nr-data.net:4317 + headers: + api-key: ${NEW_RELIC_LICENSE_KEY} +``` + +### Honeycomb + +```yaml +exporters: + otlp: + endpoint: api.honeycomb.io:443 + headers: + x-honeycomb-team: ${HONEYCOMB_API_KEY} +``` + +### Lightstep + +```yaml +exporters: + otlp: + endpoint: ingest.lightstep.com:443 + headers: + lightstep-access-token: ${LIGHTSTEP_ACCESS_TOKEN} +``` + +## Service Mesh Integration + +If using a service mesh like Istio or Linkerd: + +### Istio + +Istio automatically generates traces for service-to-service communication. Configure gateway components to propagate trace context: + +```yaml +tracing: + enabled: true + endpoint: istio-telemetry.istio-system:4317 +``` + +### Linkerd + +Linkerd integrates with Jaeger via OpenTelemetry: + +```yaml +tracing: + enabled: true + endpoint: linkerd-collector.linkerd:4317 +``` + diff --git a/en/docs/api-gateway/1.2.0/observability/tracing/best-practices-and-troubleshooting.md b/en/docs/api-gateway/1.2.0/observability/tracing/best-practices-and-troubleshooting.md new file mode 100644 index 000000000..f1096c715 --- /dev/null +++ b/en/docs/api-gateway/1.2.0/observability/tracing/best-practices-and-troubleshooting.md @@ -0,0 +1,183 @@ +--- +title: "Tracing Best Practices and Troubleshooting" +description: "Best practices for distributed tracing in API Platform Gateway: sampling strategies, production recommendations, and troubleshooting guidance." +canonical_url: https://wso2.com/api-platform/docs/api-gateway/observability/tracing/best-practices-and-troubleshooting/ +md_url: https://wso2.com/api-platform/docs/api-gateway/observability/tracing/best-practices-and-troubleshooting.md +tags: + - api-gateway + - observability + - tracing + - troubleshooting +author: WSO2 API Platform Documentation Team +last_updated: 2026-06-17 +content_type: "how-to" +--- + +# Best Practices and Troubleshooting + +## Best Practices + +### Development +- Use 100% sampling rate (`sampling_rate: 1.0`) +- Enable debug output in OTLP collector +- Use Jaeger for quick trace visualization +- Keep trace data for 1-7 days + +### Production +- **Use managed services** (Datadog, New Relic, etc.) to reduce operational overhead +- **Implement appropriate sampling** (1-10% depending on traffic volume) +- **Enable TLS** for OTLP connections +- **Set resource limits** on OTLP collector +- **Monitor collector health** and performance +- **Implement trace retention policies** based on compliance and storage costs +- **Use tail-based sampling** to keep important traces (errors, slow requests) + +### Security +- Enable TLS for trace transmission +- Sanitize sensitive data from trace attributes +- Implement proper access controls for trace viewing +- Regularly audit who accesses trace data +- Consider data residency requirements + +### Performance +- Use appropriate sampling rates to balance visibility and overhead +- Configure batch settings to optimize network usage +- Monitor gateway component overhead from tracing +- Use asynchronous trace export (default with OTLP) +- Consider using tail-based sampling for high-volume environments + +### Sampling Strategy + +Choose sampling based on traffic volume: + +| Traffic Volume | Sampling Rate | Use Case | +|---------------|---------------|----------| +| < 100 req/s | 100% (1.0) | Full visibility, low overhead | +| 100-1000 req/s | 10-50% (0.1-0.5) | Balanced visibility and cost | +| 1000-10000 req/s | 1-10% (0.01-0.1) | Cost-effective, statistical sampling | +| > 10000 req/s | 0.1-1% (0.001-0.01) | Minimal overhead, error sampling | + +**Note:** Always use 100% sampling for errors using tail-based sampling. + +## Troubleshooting + +### Traces Not Appearing in Jaeger + +**1. Verify tracing is enabled in configuration:** +```bash +cat gateway/configs/config.toml | grep -A5 "tracing" +``` + +Ensure `enabled = true`. + +**2. Check OTLP Collector is running:** +```bash +docker ps | grep otel-collector +``` + +**3. View OTLP Collector logs:** +```bash +docker logs otel-collector +``` + +Look for connection errors or export failures. + +**4. Check Jaeger is running:** +```bash +docker ps | grep jaeger +curl http://localhost:16686/ +``` + +**5. Verify network connectivity:** +```bash +docker exec policy-engine ping otel-collector +docker exec otel-collector ping jaeger +``` + +**6. Check gateway component logs for trace export errors:** +```bash +docker logs policy-engine | grep -i trace +docker logs gateway-controller | grep -i trace +``` + +### Traces Are Incomplete or Missing Spans + +**1. Check sampling rate** - ensure it's not too low +**2. Verify all components are configured** to export traces +**3. Check for trace context propagation issues** - ensure headers are preserved +**4. Look for timeout errors** in OTLP collector logs + +### High Trace Export Overhead + +**1. Reduce sampling rate:** +```toml +[tracing] +sampling_rate = 0.1 # Reduce from 1.0 to 0.1 +``` + +**2. Increase batch size:** +```toml +[tracing] +batch_timeout = "5s" +max_export_batch_size = 2048 +``` + +**3. Use tail-based sampling** in OTLP collector to sample only important traces + +### Traces Have Incorrect Timing + +- **Ensure system clocks are synchronized** across all containers (use NTP) +- **Check for clock skew** in trace timeline view +- **Verify trace context propagation** is working correctly + +### Cannot Access Jaeger UI + +**1. Verify Jaeger is running:** +```bash +docker ps | grep jaeger +``` + +**2. Check Jaeger logs:** +```bash +docker logs jaeger +``` + +**3. Ensure port 16686 is not blocked:** +```bash +curl http://localhost:16686/ +``` + +## Integration with Logging + +Traces and logs work together for comprehensive observability: + +### Correlating Traces and Logs + +1. **Trace ID in Logs**: Gateway components include trace IDs in log entries +2. **Find Trace from Log**: Copy trace ID from log entry and search in Jaeger +3. **Find Logs from Trace**: Copy trace ID from Jaeger and search in log viewer + +Example log entry with trace ID: +```json +{ + "level": "info", + "ts": "2025-12-19T10:30:45.456Z", + "msg": "Policy executed", + "trace_id": "0af7651916cd43dd8448eb211c80319c", + "span_id": "b7ad6b7169203331", + "policy": "modify-headers" +} +``` + +### Using Both Stacks + +Enable both logging and tracing profiles: + +```bash +docker compose --profile logging --profile tracing up -d +``` + +This provides complete observability: +- **Traces**: Request flow and performance +- **Logs**: Detailed event information and debugging + diff --git a/en/docs/api-gateway/1.2.0/observability/tracing/configuration.md b/en/docs/api-gateway/1.2.0/observability/tracing/configuration.md new file mode 100644 index 000000000..5a9bc81f1 --- /dev/null +++ b/en/docs/api-gateway/1.2.0/observability/tracing/configuration.md @@ -0,0 +1,211 @@ +--- +title: "Tracing Configuration Options" +description: "Configure distributed tracing for API Platform Gateway: sampling rates, OTLP collector endpoints, and batch export settings." +canonical_url: https://wso2.com/api-platform/docs/api-gateway/observability/tracing/configuration/ +md_url: https://wso2.com/api-platform/docs/api-gateway/observability/tracing/configuration.md +tags: + - api-gateway + - observability + - tracing +author: WSO2 API Platform Documentation Team +last_updated: 2026-06-17 +content_type: "reference" +--- + +# Tracing Configuration + +## Configuration Options + +### Adjusting Sampling Rate + +To reduce trace volume in high-traffic environments, adjust the sampling rate: + +```toml +[tracing] +sampling_rate = 0.1 # Sample 10% of requests +``` + +Sampling strategies: +- `1.0` (100%): Sample all requests - recommended for development and low-traffic environments +- `0.5` (50%): Sample half of requests - moderate traffic +- `0.1` (10%): Sample 10% of requests - high traffic +- `0.01` (1%): Sample 1% of requests - very high traffic + +**Note:** Lower sampling rates reduce overhead but may miss important traces. + + +### Custom Service Names + +Customize service names for better identification: + +```toml +[policy_engine] +service_name = "policy-engine-prod-us-east-1" +``` + +### Batch Configuration + +Optimize batch settings for your environment: + +```toml +[tracing] +batch_timeout = "5s" # Wait up to 5s before exporting +max_export_batch_size = 1024 # Export up to 1024 spans per batch +``` + +**Lower timeout**: Faster trace visibility, more network overhead +**Higher timeout**: Better batching efficiency, slower trace visibility + +## Customizing OpenTelemetry Collector + +The OTLP Collector configuration is located at: +``` +gateway/observability/otel-collector/config.yaml +``` + +### Configuration Structure + +The configuration consists of three main sections: + +#### Receivers +Define how traces are received: + +```yaml +receivers: + otlp: + protocols: + grpc: + endpoint: 0.0.0.0:4317 + http: + endpoint: 0.0.0.0:4318 +``` + +#### Processors +Transform and enrich traces: + +```yaml +processors: + # Batch spans for efficiency + batch: + timeout: 1s + send_batch_size: 1024 + + # Add resource attributes + resource: + attributes: + - key: environment + value: production + action: upsert + - key: cluster + value: us-west-2 + action: upsert + + # Memory limiter to prevent OOM + memory_limiter: + check_interval: 1s + limit_mib: 512 + + # Sampling processor + probabilistic_sampler: + sampling_percentage: 10 # Sample 10% of traces +``` + +#### Exporters +Define where traces are sent: + +```yaml +exporters: + # Send to Jaeger + otlp: + endpoint: jaeger:4317 + tls: + insecure: true + + # Debug output to console + debug: + verbosity: detailed + sampling_initial: 5 + sampling_thereafter: 200 +``` + +#### Service Pipeline +Connect receivers, processors, and exporters: + +```yaml +service: + pipelines: + traces: + receivers: [otlp] + processors: [memory_limiter, batch, resource] + exporters: [otlp, debug] +``` + +### Example: Multi-Backend Export + +Send traces to multiple backends simultaneously: + +```yaml +exporters: + otlp/jaeger: + endpoint: jaeger:4317 + tls: + insecure: true + + otlp/tempo: + endpoint: tempo:4317 + tls: + insecure: true + + datadog: + api: + key: ${DD_API_KEY} + +service: + pipelines: + traces: + receivers: [otlp] + processors: [memory_limiter, batch] + exporters: [otlp/jaeger, otlp/tempo, datadog] +``` + +### Example: Tail-Based Sampling + +Keep all error traces but sample successful traces: + +```yaml +processors: + tail_sampling: + policies: + - name: error-traces + type: status_code + status_code: + status_codes: [ERROR] + - name: slow-traces + type: latency + latency: + threshold_ms: 1000 + - name: probabilistic + type: probabilistic + probabilistic: + sampling_percentage: 10 +``` + +## Trace Context Propagation + +The gateway components automatically propagate trace context using standard W3C Trace Context headers: + +- `traceparent`: Contains trace ID, span ID, and sampling decision +- `tracestate`: Contains vendor-specific trace information + +When making requests to the gateway, you can: + +1. **Let the gateway create a new trace** (default) +2. **Propagate your own trace context** by including trace headers: + +```bash +curl http://localhost:8080/weather/v1.0/us/seattle \ + -H "traceparent: 00-0af7651916cd43dd8448eb211c80319c-b7ad6b7169203331-01" +``` + +This allows you to trace requests across your entire system, including services before and after the gateway. + diff --git a/en/docs/api-gateway/1.2.0/observability/tracing/enabling-tracing.md b/en/docs/api-gateway/1.2.0/observability/tracing/enabling-tracing.md new file mode 100644 index 000000000..77b6d146e --- /dev/null +++ b/en/docs/api-gateway/1.2.0/observability/tracing/enabling-tracing.md @@ -0,0 +1,94 @@ +--- +title: "Enable Distributed Tracing" +description: "Enable and configure distributed tracing for API Platform Gateway in config.toml by pointing to an OTLP collector endpoint." +canonical_url: https://wso2.com/api-platform/docs/api-gateway/observability/tracing/enabling-tracing/ +md_url: https://wso2.com/api-platform/docs/api-gateway/observability/tracing/enabling-tracing.md +tags: + - api-gateway + - observability + - tracing +author: WSO2 API Platform Documentation Team +last_updated: 2026-06-17 +content_type: "how-to" +--- + +# Enabling and Disabling Tracing + +## Enabling Tracing + +### Configuration Required + +You need to enable tracing in the gateway configuration file and point it to your OTLP collector endpoint. + +The tracing configuration is located in `gateway/configs/config.toml`: + +#### Policy Engine Tracing Configuration + +```toml +[tracing] +enabled = true # Set to true to enable tracing +endpoint = "otel-collector:4317" # OTLP collector gRPC endpoint +service_version = "0.2.0" # Service version +batch_timeout = "1s" # Batch timeout for exporting spans +max_export_batch_size = 512 # Maximum spans per batch +sampling_rate = 1.0 # Sample rate (1.0 = 100%, 0.5 = 50%) +``` + +### Demonstrated Tracing Services + +The tracing services included in the Docker Compose file (Jaeger and OpenTelemetry Collector) are provided as **demonstration services** to show one possible way to collect and visualize traces. You can use them as-is for development/testing, or replace them with your own tracing solution. + +The gateway uses Docker Compose profiles to optionally enable these demonstration tracing services. + +### Start Gateway with Demonstrated Tracing Services + +To start the gateway with the demonstration tracing services enabled: + +```bash +docker compose --profile tracing up -d +``` + +This starts: +- Core gateway services (gateway-controller, policy-engine, router) - *which export traces to OTLP collector* +- OpenTelemetry Collector - *receives and processes traces* +- Jaeger - *stores and visualizes traces* + +### Start Gateway without Tracing Services + +To run only the core gateway services without the demonstration tracing stack: + +```bash +docker compose up +``` + +**Note:** If tracing is enabled in the configuration but the OTLP collector is not running, components will log warnings about failed trace exports. To completely disable tracing, set `enabled = false` in the configuration. + +### Stop Tracing Services + +To stop all services including the tracing stack: + +```bash +docker compose --profile tracing down +``` + +**Note:** Jaeger stores traces in memory by default. Stopping the service will lose all trace data. For persistent storage, configure Jaeger with a backend database (see Jaeger documentation). + +## Disabling Tracing + +To completely disable tracing: + +1. **Update configuration** in `gateway/configs/config.toml`: + +```toml +[tracing] +enabled = false +``` + +2. **Restart gateway services:** + +```bash +docker compose restart gateway-controller policy-engine router +``` + +**Note:** The router (Envoy) tracing is controlled by the gateway-controller configuration and will be disabled when the configuration is updated. + diff --git a/en/docs/api-gateway/1.2.0/observability/tracing/overview.md b/en/docs/api-gateway/1.2.0/observability/tracing/overview.md new file mode 100644 index 000000000..1688b581b --- /dev/null +++ b/en/docs/api-gateway/1.2.0/observability/tracing/overview.md @@ -0,0 +1,64 @@ +--- +title: "Distributed Tracing Overview" +description: "Overview of the distributed tracing architecture for API Platform Gateway: how components export traces through the OpenTelemetry Collector to Jaeger." +canonical_url: https://wso2.com/api-platform/docs/api-gateway/observability/tracing/overview/ +md_url: https://wso2.com/api-platform/docs/api-gateway/observability/tracing/overview.md +tags: + - api-gateway + - observability + - tracing +author: WSO2 API Platform Documentation Team +last_updated: 2026-06-17 +content_type: "concept" +--- + +# Gateway Tracing + +This guide explains how to implement and configure distributed tracing for the API Platform Gateway components. + +## Overview + +The default tracing services included in the Docker Compose configuration are **demonstration services** designed to showcase how you can observe distributed traces across gateway components in a centralized setup. These services provide a reference implementation that you can use out-of-the-box for development, testing, or as a starting point for your production tracing strategy. + +**Important**: You are free to choose any tracing or observability strategy that suits your environment and requirements. The provided setup is just one of many possible configurations. + +## Tracing Architecture + +The default tracing stack consists of: + +- **OpenTelemetry (OTLP) Collector**: Receives, processes, and exports trace data from gateway components +- **Jaeger**: Stores and visualizes distributed traces with a web UI for trace exploration and analysis + +### How It Works + +1. Gateway components (gateway-controller, policy-engine, router) are configured to export traces via OTLP (OpenTelemetry Protocol) +2. Components send trace spans to the OpenTelemetry Collector via gRPC (port 4317) or HTTP (port 4318) +3. The OTLP Collector processes traces (batching, adding resource attributes, etc.) +4. The OTLP Collector forwards traces to Jaeger for storage and visualization +5. Users can view and analyze traces through the Jaeger UI + +### What is Distributed Tracing? + +Distributed tracing tracks a request as it flows through multiple components, using a few key concepts: + +- **Trace**: Represents the entire journey of a request through the system +- **Span**: Represents a single operation within a trace (e.g., policy execution, upstream call) +- **Context Propagation**: Traces are correlated across components using trace IDs and span IDs in headers + +## In This Section + +- [Enabling and Disabling Tracing](enabling-tracing.md) — Turn tracing on or off and run the demonstration tracing services +- [Viewing Traces in Jaeger](viewing-traces-in-jaeger.md) — Search, analyze, and compare distributed traces in the Jaeger UI +- [Tracing Configuration](configuration.md) — Sampling, batching, OpenTelemetry Collector customization, and trace context propagation +- [Alternative Tracing Backends](alternative-backends.md) — Export traces to Moesif, Zipkin, Tempo, and cloud-native solutions +- [Best Practices and Troubleshooting](best-practices-and-troubleshooting.md) — Recommendations, common issues, and integration with logging + +## Additional Resources + +- [OpenTelemetry Documentation](https://opentelemetry.io/docs/) +- [Jaeger Documentation](https://www.jaegertracing.io/docs/) +- [OpenTelemetry Collector Documentation](https://opentelemetry.io/docs/collector/) +- [W3C Trace Context Specification](https://www.w3.org/TR/trace-context/) +- [Gateway Overview](../../overview.md) +- [Gateway Logging Guide](../logging.md) +- [Docker Compose Profiles](https://docs.docker.com/compose/profiles/) diff --git a/en/docs/api-gateway/1.2.0/observability/tracing/viewing-traces-in-jaeger.md b/en/docs/api-gateway/1.2.0/observability/tracing/viewing-traces-in-jaeger.md new file mode 100644 index 000000000..cc7cc2bda --- /dev/null +++ b/en/docs/api-gateway/1.2.0/observability/tracing/viewing-traces-in-jaeger.md @@ -0,0 +1,98 @@ +--- +title: "View Traces in Jaeger" +description: "Access the Jaeger UI and search for distributed traces from API Platform Gateway components." +canonical_url: https://wso2.com/api-platform/docs/api-gateway/observability/tracing/viewing-traces-in-jaeger/ +md_url: https://wso2.com/api-platform/docs/api-gateway/observability/tracing/viewing-traces-in-jaeger.md +tags: + - api-gateway + - observability + - tracing + - jaeger +author: WSO2 API Platform Documentation Team +last_updated: 2026-06-17 +content_type: "how-to" +--- + +# Viewing Traces in Jaeger + +Once you've started the gateway with the tracing profile, follow these steps to view distributed traces: + +## Step 1: Access Jaeger UI + +Open your browser and navigate to: +``` +http://localhost:16686 +``` + +## Step 2: Search for Traces + +The Jaeger UI provides several ways to search for traces: + +1. **Select a Service** from the dropdown: + - `policy-engine` - View traces from the policy engine + - `router` - View traces from the Envoy router + +2. **Select an Operation** (optional): + - Choose "all" to see all operations + - Or select a specific operation (e.g., specific policy execution) + +3. **Adjust Lookback Time Range**: + - Default: Last 1 hour + - Options: 5m, 15m, 1h, 6h, 12h, 1d, 2d, Custom + +4. **Add Filters** (optional): + - **Tags**: Filter by specific tag values (e.g., `http.status_code=500`) + - **Min/Max Duration**: Filter by trace duration + - **Limit Results**: Control number of traces returned (default: 20) + +5. Click **Find Traces** + +## Step 3: Analyze Trace Details + +Click on any trace in the results to view detailed information: + +### Trace Timeline +- **Visual timeline** showing all spans in the trace +- **Duration bars** showing relative time spent in each operation +- **Parent-child relationships** between spans +- **Color coding** by service + +### Span Details +Click on any span to see: +- **Operation name**: What operation was performed +- **Duration**: How long it took +- **Tags**: Metadata about the operation (HTTP method, status code, etc.) +- **Logs**: Events logged during the span (errors, warnings, etc.) +- **Process**: Service name, version, and host information + +### Common Use Cases + +**Finding Slow Requests:** +1. Set Min Duration filter (e.g., 1000ms) +2. Click Find Traces +3. Examine spans to identify bottlenecks + +**Debugging Errors:** +1. Filter by tag: `error=true` or `http.status_code=500` +2. Click on error traces +3. Examine span logs and tags for error details + +**Understanding Request Flow:** +1. Search for a specific trace ID (from logs or headers) +2. View the complete request path through all components +3. Identify which component handled which part of the request + +## Step 4: Trace Comparison + +You can compare multiple traces to identify patterns: +1. Select multiple traces using checkboxes +2. Click **Compare Traces** button +3. View side-by-side comparison of trace structure and timings + +## Step 5: Service Dependency Graph + +View how services interact: +1. Click **Dependencies** in the top navigation +2. Select time range +3. View graph showing service-to-service communication patterns + diff --git a/en/docs/api-gateway/1.2.0/observability/traffic-logging.md b/en/docs/api-gateway/1.2.0/observability/traffic-logging.md new file mode 100644 index 000000000..91d25d77d --- /dev/null +++ b/en/docs/api-gateway/1.2.0/observability/traffic-logging.md @@ -0,0 +1,264 @@ +--- +title: "Configure Traffic Logging" +description: "Emit a structured JSON log line for every API request from the API Platform Gateway, with no external SaaS and no policy dependency." +canonical_url: https://wso2.com/api-platform/docs/api-gateway/observability/traffic-logging/ +md_url: https://wso2.com/api-platform/docs/api-gateway/observability/traffic-logging.md +tags: + - api-gateway + - observability + - logging +author: WSO2 API Platform Documentation Team +last_updated: 2026-07-10 +content_type: "how-to" +--- + +# Traffic Logging + +## Overview + +Traffic Logging writes a single structured JSON line to **stdout** for every request handled by the +gateway — who called what, the status code, latencies, request/response headers, and (optionally) +bodies. It is designed to be picked up by any log-scraping pipeline (Fluent Bit, Loki, ELK, CloudWatch, +and so on); see [Centralized Logging](logging.md) for a reference log stack that can collect it. + +!!! note + Traffic Logging and Moesif Analytics are independent consumers of the same underlying data-capture + pipeline. You can enable either, both, or neither. + +## How it works + +Request/response header and body capture is a **shared** concern, handled by a capture pipeline called +the **collector**. The collector itself has no on/off switch of its own — it activates automatically +whenever a consumer that needs it is enabled (`analytics.enabled` or `traffic_logging.enabled`). +Traffic Logging is one such consumer: it reads whatever the collector captured and serializes it to +stdout. + +Because emission happens on Envoy's access-log path, it fires for every request Envoy terminates — +including requests denied by an auth policy before they reach any downstream logic. + +## Enabling Traffic Logging + +At minimum, enable `[traffic_logging]` and turn on whichever `[collector]` capture flags you want +reflected in the log line. Each `traffic_logging.*_headers` / `*_body` toggle only **selects among** +what `[collector]` already captured — enabling it while the matching `[collector]` flag is off has no effect. + +```toml +[collector] +request_headers = true +response_headers = true + +[traffic_logging] +enabled = true +request_headers = true +response_headers = true +``` + +With this configuration, every request to every API produces a JSON line on stdout containing request +and response headers (redacted per `masked_headers`, see below). + +## Configuration reference + +### `[collector]` + +Shared capture pipeline. Configured once; every enabled consumer (Analytics, Traffic Logging) reads +from it. + +| Parameter | Type | Default | Description | +|---|---|---|---| +| `request_body` | boolean | `false` | Capture the full request body into the collected event. | +| `response_body` | boolean | `false` | Capture the full response body into the collected event. | +| `request_headers` | boolean | `false` | Capture all request headers into the collected event. | +| `response_headers` | boolean | `false` | Capture all response headers into the collected event. | +| `ignore_path_prefixes` | array of strings | `[]` | Path prefixes for which no analytics event and no traffic-log line is produced at all — as if capture were disabled for that one request. See [Ignoring paths](#ignoring-paths). | + +!!! note + Bodies can be large. Capture is off by default for both request and response bodies — enable only + what you need, and use `traffic_logging.max_payload_size` (below) to cap what's written to the log + line. + +### `[traffic_logging]` + +| Parameter | Type | Default | Description | +|---|---|---|---| +| `enabled` | boolean | `false` | Emit a stdout JSON line for every request to every API. | +| `masked_headers` | array of strings | `["authorization", "x-api-key", "x-jwt-assertion"]` | Header names (case-insensitive) whose values are redacted as `****` in the logged `requestHeaders`/`responseHeaders`. | +| `max_payload_size` | int | `0` | Maximum bytes of request/response payload written per log line. `0` = no limit. Applied output-side only — the collector still captures the full body. | +| `request_headers` | boolean | `false` | Include captured request headers in the log line. No-op if `collector.request_headers` is `false`. | +| `request_body` | boolean | `false` | Include the captured request body. No-op if `collector.request_body` is `false`. | +| `response_headers` | boolean | `false` | Include captured response headers in the log line. No-op if `collector.response_headers` is `false`. | +| `response_body` | boolean | `false` | Include the captured response body. No-op if `collector.response_body` is `false`. | +| `exclude_fields` | array of strings | `[]` | Drop named fields from the emitted line. See [Field exclusion](#field-exclusion). | + +### `[collector.server]` (ALS transport tuning) + +Advanced settings for the Envoy → policy-engine access-log transport shared by the collector. Defaults +are sensible for most deployments. + +| Parameter | Type | Default | Description | +|---|---|---|---| +| `mode` | string | `"uds"` | Transport mode: `"uds"` (Unix domain socket) or `"tcp"`. | +| `als_plain_text` | boolean | `true` | Use plaintext gRPC (skip TLS). | +| `public_key_path` / `private_key_path` | string | `""` | TLS keypair for the ALS connection, when `als_plain_text = false`. | +| `max_message_size` | int | `1000000000` | Maximum size of a single gRPC message the ALS server accepts from Envoy. | +| `max_header_limit` | int | `8192` | Maximum size of headers processed by the ALS server. | +| `shutdown_timeout` | duration | `"600s"` | Maximum time allowed for graceful ALS server shutdown. | + +!!! note + The ALS transport's TCP port is fixed at `18090` and is not configurable — this guarantees the + controller and the policy-engine can never disagree on it. `buffer_flush_interval`, + `buffer_size_bytes`, and `grpc_request_timeout` are additional Envoy-sender-only keys under + `[collector.server]`; see `config-template.toml` for their defaults. + +## Redaction and payload control + +* **`masked_headers`** redacts matching header values (case-insensitive) to `****` in the logged + `requestHeaders`/`responseHeaders` maps. This is applied output-side by Traffic Logging only — other + consumers of the collector (such as Moesif) are unaffected and receive unmasked headers. +* **`max_payload_size`** truncates the request/response body written to the log line. Set to `0` + (default) for no limit. +* **`exclude_fields`** drops named fields from the emitted line entirely, on top of the toggles above. + +### Field exclusion + +`exclude_fields` accepts top-level keys (e.g. `"latencies"`, `"requestHeaders"`) or dotted paths of +arbitrary depth into nested JSON objects, for example: + +```toml +[traffic_logging] +exclude_fields = ["requestBody", "responseBody", "requestHeaders.authorization"] +``` + +* Sub-keys immediately under `requestHeaders`/`responseHeaders` match case-insensitively (HTTP header + names); every other path segment matches case-sensitively. +* A path can reach into a nested object produced by a `properties` expression that resolves to a map, + for example `properties.claims.internal_debug`. + +## Ignoring paths + +`collector.ignore_path_prefixes` suppresses the analytics event — and therefore the traffic-log +line — for matching paths, such as health checks or metrics scrapes: + +```toml +[collector] +ignore_path_prefixes = ["/health", "/metrics"] +``` + +This is enforced by Envoy itself via an access-log filter, so the policy-engine never even receives +the request for a matching path. + +## Custom properties + +`[traffic_logging.properties]` adds extra key → value pairs under a top-level `properties` object in +the log line. A value prefixed `$ctx:` is evaluated as a CEL expression against the collected request +context; any other value is emitted as a literal string. + +```toml +[traffic_logging.properties] +env = "prod" +apiName = "$ctx:api.name" +status = "$ctx:response.status" +subject = "$ctx:auth.subject != '' ? auth.subject : 'anonymous'" +tenant = "$ctx:'tenant' in auth.property ? auth.property['tenant'] : ''" +appId = "$ctx:'applicationId' in metadata ? metadata['applicationId'] : ''" +``` + +Available variables: + +| Namespace | Variables | +|---|---| +| Request | `request.path`, `request.method`, `request.id`, `request.header['']` (masked per `masked_headers`) | +| Response | `response.status`, `response.header['']` (masked per `masked_headers`) | +| API | `api.id`, `api.name`, `api.version`, `api.context`, `api.kind`, `project.id` | +| Target | `target.statusCode`, `target.destination` | +| Application | `application.id`, `application.name`, `application.owner`, `application.keyType` — populated only when an auth policy that stamps application identity (currently `api-key-auth`) ran | +| Auth | `auth.subject`, `auth.type`, `auth.issuer`, `auth.credential_id`, `auth.token_id`, `auth.audience` (list), `auth.scopes` (list), `auth.property['']` (map), `auth.authenticated`, `auth.authorized` | +| Generic metadata | `metadata['']` — any key any policy (including third-party/Python policies) has written into shared request metadata | + +`auth.*` is populated generically for **any** authenticated request, regardless of which auth policy +(`jwt-auth`, `opaque-token-auth`, `api-key-auth`, and so on) ran. For an unauthenticated or denied +request, every `auth.*` scalar variable resolves to its zero value rather than erroring, so +`auth.subject != "" ? auth.subject : "anonymous"` is a safe pattern for a property that should always +resolve to something. + +!!! note + Indexing into a map or list variable (`auth.property['']`, `metadata['']`, + `auth.audience[0]`) still raises an error if the key or index is absent, and that property is + silently omitted from the line. Guard map/list access with the `in` operator, as shown in the + `tenant` and `appId` examples above. + +!!! note + Unlike headers, `metadata['']` has **no masking configuration**. Shared request metadata is a + generic, schema-less bag that any policy can write to, so any value referenced from + `properties` is emitted verbatim. Avoid referencing metadata keys that may contain sensitive + values. + +A `$ctx:` expression that references a variable this surface doesn't expose (for example, a typo) +fails to compile when the gateway starts, is logged once, and that property is permanently omitted +from every line — it does not cause requests to fail. + +## Example log line + +```json +{ + "timestamp": "2026-07-10T09:12:33.482Z", + "correlationId": "5f6b6e2a-2f38-4b7a-9c2f-6b6a2f384b7a", + "status": 200, + "api": { + "id": "01998f3e-...", + "name": "Weather-API", + "version": "1.0.0", + "context": "/weather", + "kind": "REST" + }, + "operation": { + "method": "GET", + "path": "/current" + }, + "target": { + "statusCode": 200, + "destination": "https://backend.example.com/current" + }, + "application": { + "id": "app-123", + "name": "Mobile App", + "owner": "jdoe", + "keyType": "PRODUCTION" + }, + "client": { + "ip": "203.0.113.10", + "userAgent": "curl/8.4.0" + }, + "latencies": { + "durationUs": 18342, + "requestMediationLatencyUs": 412, + "responseMediationLatencyUs": 298, + "backendLatencyUs": 17102 + }, + "requestHeaders": { + "authorization": "****", + "user-agent": "curl/8.4.0" + }, + "responseHeaders": { + "content-type": "application/json" + }, + "properties": { + "env": "prod", + "apiName": "Weather-API", + "subject": "jdoe@example.com" + } +} +``` + +Fields whose values are entirely empty (for example `application` on an unauthenticated request) are +omitted from the line rather than emitted as `{}`. + +## Kubernetes / Helm + +The Helm chart renders `[collector]` (all capture flags, `ignore_path_prefixes`), +`[collector.server].mode`, and the `[traffic_logging]` flow-selection booleans and `exclude_fields` +from `.Values.gateway.config`. + +!!! note + `[traffic_logging.properties]` is not yet rendered by the Helm chart. If you need CEL-derived + custom properties in a Helm-deployed gateway, supply a custom `config.toml` override instead of + Helm values. diff --git a/en/docs/api-gateway/1.2.0/overview.md b/en/docs/api-gateway/1.2.0/overview.md new file mode 100644 index 000000000..9e24507a0 --- /dev/null +++ b/en/docs/api-gateway/1.2.0/overview.md @@ -0,0 +1,89 @@ +--- +title: "API Platform Gateway Overview" +description: "Learn the architecture and components of API Platform Gateway: Gateway Controller, Gateway Runtime, Policy Engine, default ports, and the ap CLI." +canonical_url: https://wso2.com/api-platform/docs/api-gateway/overview/ +md_url: https://wso2.com/api-platform/docs/api-gateway/overview.md +tags: + - api-gateway + - architecture + - api-management +author: WSO2 API Platform Documentation Team +last_updated: 2026-06-11 +content_type: "concept" +--- + +# API Platform Gateway Overview + +API Platform Gateway is the complete API gateway system for managing, securing, and routing API traffic to your backend services. + +## Components + +| Component | Purpose | +|-----------|---------| +| **Gateway-Controller** | Control plane that manages API configurations and dynamically configures the Router | +| **Gateway-Runtime** | Data plane (Envoy Proxy) that routes HTTP/HTTPS traffic to backend services and Processes requests/responses through configurable policies (authentication, rate limiting, etc.)| +| **Policy Builder** | Build-time tooling for compiling custom policy implementations | + +### CLI Tool (ap) + +The `ap` CLI provides a command-line interface for managing gateways, APIs, and MCP proxies. Key capabilities include: + +- Gateway management (add, list, remove, health check) +- API lifecycle management (apply, list, get, delete) +- MCP proxy management (generate, list, get, delete) + +For the complete list of CLI commands and usage examples, see the [CLI Reference](../../tools/cli/reference.md). + +## Default Ports + +| Port | Service | Description | +|------|---------|-------------| +| 8080 | Router | HTTP traffic | +| 8443 | Router | HTTPS traffic | +| 9090 | Gateway-Controller | REST API | +| 9094 | Gateway-Controller Admin | Health and admin endpoints | + +## Architecture + +![Architecture diagram showing Gateway Controller with REST API, Configuration Transformer, xDS Server, and Repository Controller above, and Gateway Runtime with Envoy Router, Policy Engine, and Python Executor below](../../assets/img/api-gateway/gateway-architecture.png) + + +The API Gateway consists of two main components: **Gateway Controller** and **Gateway Runtime**. + +- **Gateway Controller** is the control plane that manages API configurations and pushes them to the Gateway Runtime via the xDS protocol. +- **Gateway Runtime** is the data plane that processes API traffic. It contains three sub-components: + - **Router** (Envoy proxy) — handles traffic routing, load balancing, and TLS termination. + - **Policy Engine** — an ext_proc filter that executes request/response policies. **Go-based** policies are compiled into the Policy Engine binary at image build time by the Gateway Builder. + - **Python Executor** — a dedicated runtime component that dynamically evaluates Python-based policies, allowing developers to leverage the extensive Python ecosystem for custom logic. + +The Gateway Controller configures the Gateway Runtime by pushing API and route configurations through xDS. When a request arrives, the Router forwards it to the Policy Engine for policy evaluation, then routes it to the upstream backend. + +### How it works + +1. User verifies the Gateway-Controller admin health endpoint +2. User submits API configuration (YAML/JSON) to the Gateway-Controller REST API +3. Gateway-Controller validates and persists the configuration +4. Router receives the updated configuration and starts routing traffic + +### Policies + +Policies allow you to intercept and transform API traffic at the Gateway-Runtime (Envoy Proxy). The Gateway offers a flexible, dual-language approach to policy development, empowering teams to build custom API logic tailored to their performance and ecosystem needs. + +- **Go:** Compiled directly into the Policy Engine binary, Go provides maximum execution performance, strict type safety, and minimal latency for critical path operations. +- **Python:** Executed dynamically by the integrated Python Executor, Python is particularly ideal for AI/ML use cases and complex data transformations due to its extensive ecosystem support and specialized libraries. + +Policies can be applied to request and response flows to handle concerns like authentication, rate limiting, header manipulation, and more. + +The complete and up-to-date policy catalogue — with configuration references and examples — is maintained in the gateway-controllers repository: https://github.com/wso2/gateway-controllers/blob/main/docs/README.md + +You can extend the gateway with your own policies or include specific policies from the catalogue by building a custom gateway image using the `ap` CLI. See [Building the Gateway with Custom Policies](./policies/custom-policies/building-gateway-with-custom-policies.md). + +## High Availability Setup + +In a production HA deployment: + +- **Gateway Controller** instances connect to a shared external database (**PostgreSQL** or **SQL Server**) for persistent storage of API configurations, subscriptions, and other metadata. +- **Gateway Runtime** instances connect to a shared **Redis** instance used for distributed rate limiting, ensuring rate limit counters are synchronized across all runtime instances. + +![HA diagram with two Gateway Controller replicas sharing PostgreSQL, three Gateway Runtime replicas sharing Redis, and all runtimes reporting to Moesif analytics](../../assets/img/api-gateway/gateway-ha-setup.png) + diff --git a/en/docs/api-gateway/1.2.0/performance/gateway-runtime-with-four-cpus.md b/en/docs/api-gateway/1.2.0/performance/gateway-runtime-with-four-cpus.md new file mode 100644 index 000000000..918f3f0d5 --- /dev/null +++ b/en/docs/api-gateway/1.2.0/performance/gateway-runtime-with-four-cpus.md @@ -0,0 +1,65 @@ +--- +title: "Gateway Runtime with Four CPUs - Performance Results" +description: "View API Platform Gateway performance benchmark results with a four-CPU gateway runtime, including throughput, average response time, and percentile response times." +canonical_url: https://wso2.com/api-platform/docs/api-gateway/performance/gateway-runtime-with-four-cpus/ +md_url: https://wso2.com/api-platform/docs/api-gateway/performance/gateway-runtime-with-four-cpus.md +tags: + - api-gateway + - performance + - benchmarks + - gateway-runtime +author: WSO2 API Platform Documentation Team +last_updated: 2026-07-01 +content_type: "reference" +--- + +# Gateway runtime with four CPUs + +The table below displays the resource allocations for the gateway-related components used in the performance tests. + +| Component | CPU | Memory | Router Concurrency | GOMAXPROCS | +| ------------------ | --- | ------ | ------------------ | ---------- | +| Gateway Controller | 1 | 2 GB | — | — | +| Gateway Runtime | 4 | 2 GB | 4 | 4 | + +## Throughput (requests/sec) vs. concurrent users + +The graph below shows how gateway throughput changes as concurrent users increase for the API without policies and the API with mediation policies. + +![Line chart of gateway throughput in requests per second versus concurrent users at 100, 500, 800, and 1000 users, comparing the API without policies and the API with mediation policies on a four-CPU gateway runtime](../../../assets/img/api-gateway/performance/four-core-Throughput-vs-Concurrent-Users.svg){ width="900" } + +**Key observations:** + +- Maximum throughput for both APIs occurs at 100 concurrent users on this four-CPU configuration. +- Throughput decreases slightly as concurrent users increase beyond 100 due to resource contention. +- Both APIs sustain strong throughput from 100 through 1000 concurrent users on the four-CPU gateway runtime. + +## Average response time (ms) vs. concurrent users + +The graph below shows how average response time changes for both APIs as concurrent users increase. The backend delay was configured to 0 ms for these tests. + +![Line chart of average response time in milliseconds versus concurrent users at 100, 500, 800, and 1000 users, comparing the API without policies and the API with mediation policies on a four-CPU gateway runtime](../../../assets/img/api-gateway/performance/four-core-Average-Response-Time-vs-Concurrent-Users.svg){ width="900" } + +**Key observations:** + +- Average response time increases as concurrent users grow. +- The four-CPU configuration improves response times compared with the two-CPU results under comparable load. + +## Response time percentiles vs. concurrent users + +The graphs below show the 90th, 95th, and 99th percentile response times at 0 ms backend delay. Percentile values indicate the response time below which that percentage of requests completed, for example, the 99th percentile is the response time exceeded by only 1% of requests. + +![Line chart of 90th, 95th, and 99th percentile response times in milliseconds versus concurrent users for the API without policies on a four-CPU gateway runtime](../../../assets/img/api-gateway/performance/four-core-AP-%20with-8-routes-Response-Time-vs-Concurrent-Users.svg){ width="900" } + +**Key observations:** + +- 90th, 95th, and 99th percentile response times increase as concurrent users grow. +- The four-CPU configuration yields lower percentile values at high concurrency than the two-CPU configuration. + +![Line chart of 90th, 95th, and 99th percentile response times in milliseconds versus concurrent users for the API with mediation policies on a four-CPU gateway runtime](../../../assets/img/api-gateway/performance/four-core-API-with-8-routes-Mediation-Policy-Response-Time-vs-Concurrent-Users.svg){ width="900" } + +**Key observations:** + +- Percentile trends follow the same pattern as concurrent users increase across the test range. + +Test scenario results in CSV format are available [here](https://raw.githubusercontent.com/wso2/api-platform/refs/heads/main/gateway/perf/api-gateway-1.1.0-perf-test-results/four-core-results-summary.csv). \ No newline at end of file diff --git a/en/docs/api-gateway/1.2.0/performance/gateway-runtime-with-two-cpus.md b/en/docs/api-gateway/1.2.0/performance/gateway-runtime-with-two-cpus.md new file mode 100644 index 000000000..6810ceede --- /dev/null +++ b/en/docs/api-gateway/1.2.0/performance/gateway-runtime-with-two-cpus.md @@ -0,0 +1,76 @@ +--- +title: "Gateway Runtime with Two CPUs - Performance Results" +description: "View API Platform Gateway performance benchmark results with a two-CPU gateway runtime, including throughput, average response time, percentile response times, and varying payload sizes." +canonical_url: https://wso2.com/api-platform/docs/api-gateway/performance/gateway-runtime-with-two-cpus/ +md_url: https://wso2.com/api-platform/docs/api-gateway/performance/gateway-runtime-with-two-cpus.md +tags: + - api-gateway + - performance + - benchmarks + - gateway-runtime +author: WSO2 API Platform Documentation Team +last_updated: 2026-07-01 +content_type: "reference" +--- + +# Gateway runtime with two CPUs + +The table below displays the resource allocations for the gateway-related components used in the performance tests. + +| Component | CPU | Memory | Router Concurrency | GOMAXPROCS | +| ------------------ | --- | ------ | ------------------ | ---------- | +| Gateway Controller | 1 | 2 GB | — | — | +| Gateway Runtime | 2 | 2 GB | 2 | 2 | + +## Throughput (requests/sec) vs. concurrent users + +The graph below shows how gateway throughput changes as concurrent users increase for the API without policies and the API with mediation policies. + +![Line chart of gateway throughput in requests per second versus concurrent users at 100, 500, 800, and 1000 users, comparing the API without policies and the API with mediation policies on a two-CPU gateway runtime](../../../assets/img/api-gateway/performance/two-core-throughput-vs-concurrent-users.svg){ width="900" } + +**Key observations:** + +- Maximum throughput for both APIs occurs at 100 concurrent users on this two-CPU configuration. +- Throughput decreases as concurrent users increase beyond 100 due to resource contention on the gateway runtime. + +## Average response time (ms) vs. concurrent users + +The graph below shows how average response time changes for both APIs as concurrent users increase. The backend delay was configured to 0 ms for these tests. + +![Line chart of average response time in milliseconds versus concurrent users at 100, 500, 800, and 1000 users, comparing the API without policies and the API with mediation policies on a two-CPU gateway runtime](../../../assets/img/api-gateway/performance/two-core-average-response-time-vs-concurrent-users.svg){ width="900" } + +**Key observations:** + +- Average response time increases as concurrent users grow due to resource contention on the gateway runtime. + +## Response time percentiles vs. concurrent users + +The graphs below show the 90th, 95th, and 99th percentile response times at 0 ms backend delay. Percentile values indicate the response time below which that percentage of requests completed, for example, the 99th percentile is the response time exceeded by only 1% of requests. + +![Line chart of 90th, 95th, and 99th percentile response times in milliseconds versus concurrent users for the API without policies on a two-CPU gateway runtime](../../../assets/img/api-gateway/performance/two-core-api-with-8-routes-Percentile-Response-Time-vs-Concurrent-Users.svg){ width="900" } + +**Key observations:** + +- 90th, 95th, and 99th percentile response times increase as concurrent users grow. +- Percentile values represent the response time below which that percentage of requests completed. +- Higher concurrency widens the spread between lower and upper percentiles. + +![Line chart of 90th, 95th, and 99th percentile response times in milliseconds versus concurrent users for the API with mediation policies on a two-CPU gateway runtime](../../../assets/img/api-gateway/performance/two-core-API-with-8-routes-Mediation-policy-Percentile-Response-Time-vs-Concurrent-Users.svg){ width="900" } + +**Key observations:** + +- Percentile measurements include Set Header policy execution on each request and response. +- Percentile trends follow the same pattern as concurrent users increase across the test range. + +## Throughput (requests/sec) vs. concurrent users for varying payloads + +The graph below shows how throughput changes with concurrent users for payload sizes of 50 B, 1 KiB, 10 KiB, and 100 KiB. + +![Line chart of gateway throughput in requests per second versus concurrent users for payload sizes of 50 B, 1 KiB, 10 KiB, and 100 KiB on a two-CPU gateway runtime](../../../assets/img/api-gateway/performance/two-core-Throughput-vs-Concurrent-Users-by-Payload-Size.svg){ width="900" } + +**Key observations:** + +- Throughput varies with payload size; smaller payloads support higher request rates. +- For each payload size, throughput decreases as concurrent users increase. + +Test scenario results in CSV format are available [here](https://raw.githubusercontent.com/wso2/api-platform/refs/heads/main/gateway/perf/api-gateway-1.1.0-perf-test-results/two-core-results-summary.csv). diff --git a/en/docs/api-gateway/1.2.0/performance/overview.md b/en/docs/api-gateway/1.2.0/performance/overview.md new file mode 100644 index 000000000..587c14fa1 --- /dev/null +++ b/en/docs/api-gateway/1.2.0/performance/overview.md @@ -0,0 +1,61 @@ +--- +title: "API Platform Gateway Performance" +description: "Review WSO2 API Platform Gateway performance test methodology, deployment architecture, metrics, and benchmark results for two-CPU and four-CPU gateway runtime configurations." +canonical_url: https://wso2.com/api-platform/docs/api-gateway/performance/overview/ +md_url: https://wso2.com/api-platform/docs/api-gateway/performance/overview.md +tags: + - api-gateway + - performance + - benchmarks + - api-management +author: WSO2 API Platform Documentation Team +last_updated: 2026-07-01 +content_type: "overview" +--- + +# API Platform Gateway Performance + +The performance of the WSO2 API Platform Gateway was evaluated using the following APIs, both of which invoke a simple Netty HTTP Echo Service. As the name suggests, the Netty service echoes back any request it receives. + +- **API without policies**: An API configured with eight routes that directly forward requests to the backend service through the gateway. +- **API with mediation policies**: An API configured with the same eight routes, with a Set Header policy applied to both the request and response flows. + +The performance tests were conducted with 100, 500, 800, and 1000 concurrent users, where concurrent users represent multiple clients accessing the gateway simultaneously. To evaluate the impact of different message sizes, tests were performed using payload sizes of 50 B, 1 KiB, 10 KiB, and 100 KiB. The backend response delay was configured to 0 ms. + +Apache JMeter was used as the test client. Each test scenario was executed for 15 minutes, including a 3-minute warm-up period. Performance metrics were calculated after excluding the warm-up period from the analysis. + +The following key metrics were used to evaluate gateway performance: + +- **Throughput**: The number of API requests processed by the gateway per unit of time (requests per second). +- **Response time**: The end-to-end time taken to process an API request. The complete response time distribution, including the 90th, 95th, and 99th percentile response times, was recorded and analyzed. + +## Deployment used for the test + +The diagram below shows the deployment architecture used for the performance tests documented here. + +![Performance test deployment architecture showing Apache JMeter client and servers, Netty HTTP backend, and WSO2 API Platform Gateway on separate EC2 instances connected in a test topology](../../../assets/img/api-gateway/performance/deployment-used-for-test.png){ width="900" } + +| Component | EC2 Instance Type | vCPU | Memory (GiB) | +| ------------------------- | ----------------- | :--: | :----------: | +| Apache JMeter Client | `c5.2xlarge` | 8 | 16 | +| Apache JMeter Servers | `c5.2xlarge` | 8 | 16 | +| Netty HTTP Backend | `c5.2xlarge` | 8 | 16 | +| WSO2 API Platform Gateway | `c5.4xlarge` | 16 | 32 | + +- The operating system is Amazon Linux 2023.11. +- Java version is Temurin JDK 21. + +## Performance test scripts + +All scripts used to analyze results are in the following repository. + +- [https://github.com/wso2/performance-common](https://github.com/wso2/performance-common). + +## Results + +The tests were executed using the user counts and payload sizes described above across two concurrency levels. For each concurrency level, the gateway runtime was allocated the corresponding CPU resources before deploying the API Gateway test configurations. The table below summarizes the test scenarios covered in this document. + +| Test Scenario | CPU Allocation (Gateway Controller) | CPU Allocation (Gateway Runtime) | Router Concurrency | Test Results | +| ------------- | ----------------------------------- | -------------------------------- | ------------------ | ------------ | +| 1 | 1 | 2 | 2 | [Gateway runtime with two CPUs](./gateway-runtime-with-two-cpus.md) | +| 2 | 1 | 4 | 4 | [Gateway runtime with four CPUs](./gateway-runtime-with-four-cpus.md) | diff --git a/en/docs/api-gateway/1.2.0/policies/custom-policies/building-gateway-with-custom-policies.md b/en/docs/api-gateway/1.2.0/policies/custom-policies/building-gateway-with-custom-policies.md new file mode 100644 index 000000000..d25e23b11 --- /dev/null +++ b/en/docs/api-gateway/1.2.0/policies/custom-policies/building-gateway-with-custom-policies.md @@ -0,0 +1,291 @@ +--- +title: "Build a Gateway Image with Custom Policies" +description: "Use API Platform CLI to bundle custom policies into an API Platform Gateway Docker image for deployment." +canonical_url: https://wso2.com/api-platform/docs/api-gateway/policies/custom-policies/building-gateway-with-custom-policies/ +md_url: https://wso2.com/api-platform/docs/api-gateway/policies/custom-policies/building-gateway-with-custom-policies.md +tags: + - api-gateway + - policies + - custom-policies + - cli +author: WSO2 API Platform Documentation Team +last_updated: 2026-06-24 +content_type: "how-to" +--- + +# Building the Gateway with Custom Policies + +## Install the AP CLI Tool + +The `ap` CLI tool is used to build a custom gateway image with your own policies. Download the binary for your platform from the [AP CLI releases page](https://github.com/wso2/api-platform/releases/tag/ap%2Fv0.7.0) and follow the steps below to install it. + +=== "macOS / Linux" + + **Step 1: Extract the binary** + + After downloading the zip file for your platform, extract it: + + ```bash + unzip ap-darwin-amd64-v0.7.0.zip # replace with your downloaded filename + ``` + + **Step 2: Move the binary to a bin directory** + + ```bash + mkdir -p ~/bin + mv ap ~/bin/ + ``` + + **Step 3: Add to PATH** + + Add the following line to your `~/.zshrc` or `~/.bashrc`: + + ```bash + export PATH="$HOME/bin:$PATH" + ``` + + **Step 4: Reload your shell** + + ```bash + source ~/.zshrc # or source ~/.bashrc + ``` + + **Step 5: Verify the installation** + + ```bash + ap --version + ``` + +=== "Windows" + + **Step 1: Extract the binary** + + After downloading the zip file, right-click it and select **Extract All**, or run in PowerShell: + + ```powershell + Expand-Archive -Path ap-windows-amd64.zip -DestinationPath ap-windows-amd64 + ``` + + **Step 2: Move the binary to a bin directory** + + ```powershell + New-Item -ItemType Directory -Force -Path "$HOME\bin" + Move-Item ap-windows-amd64\ap.exe "$HOME\bin\ap.exe" + ``` + + **Step 3: Add to PATH** + + Run the following in PowerShell to permanently add `~/bin` to your user PATH: + + ```powershell + [Environment]::SetEnvironmentVariable("Path", $env:Path + ";$HOME\bin", "User") + ``` + + **Step 4: Reload your shell** + + Close and reopen PowerShell for the PATH change to take effect. + + **Step 5: Verify the installation** + + ```powershell + ap --version + ``` + +## Configure the Build File + +The `build.yaml` file is already included in the unzipped gateway package. It declares the gateway version and the list of policies to include. Edit this file to add your custom policies before building the gateway image. + +!!! tip "Sample Custom Policies" + WSO2 provides a set of sample custom policies to help you get started. You can find them in the [api-platform sample policies repository](https://github.com/wso2/api-platform/tree/main/gateway/sample-policies). + +### Structure + +```yaml +version: v1 +gateway: + version: 1.0.0 +policies: + - name: + gomodule: @ # for policy hub managed policies + - name: + filePath: # for custom policies +``` + +Each policy entry uses one of two source types: + +| Field | Description | +|---|---| +| `gomodule` | Go module reference for policy hub managed policies (e.g., `github.com/wso2/gateway-controllers/policies/cors@v1`) | +| `filePath` | Relative path from `build.yaml` to a local custom policy directory | + +### Adding a Custom Policy + +Your custom policy can reside anywhere on the filesystem. Use a relative path from the `build.yaml` file to point to it. + +For example, if your directory layout is: + +```text +my-gateway/ +├── build.yaml +└── ../my-custom-policy/ # policy lives outside the gateway directory + ├── policy-definition.yaml + └── mypolicy.go +``` + +Add the policy to `build.yaml` using a relative `filePath`: + +```yaml +policies: + - name: my-custom-policy + filePath: ../my-custom-policy +``` + +!!! note + The path in `filePath` is always relative to the location of `build.yaml`, not the directory from which you run the `ap` command. + +### Overriding the Base Images + +`ap gateway image build` resolves three images during a build: the **builder** image that performs the build, and the **controller** and **runtime** base images that the produced images extend. By default, all three are pulled from the public WSO2 GitHub Container Registry (GHCR) at the version declared in `gateway.version`. + +You can override any of them independently by adding an `images` block under `gateway`. This is optional for standard builds that use the public images, but **required for WSO2 subscription users**, who must point these images at the WSO2 private registry (see below). It is also useful for air-gapped environments and internal registry mirrors. + +| Key | Used as | Default (when not set) | +|---|---|---| +| `builder` | image run to perform the build | `ghcr.io/wso2/api-platform/gateway-builder:` | +| `controller` | base image extended by the built controller | `ghcr.io/wso2/api-platform/gateway-controller:` | +| `runtime` | base image extended by the built runtime | `ghcr.io/wso2/api-platform/gateway-runtime:` | + +```yaml +version: v1 +gateway: + version: 1.0.0 + images: # optional — if omitted, the public GHCR images above are used + builder: ghcr.io/wso2/api-platform/gateway-builder:1.0.0 + controller: ghcr.io/wso2/api-platform/gateway-controller:1.0.0 + runtime: ghcr.io/wso2/api-platform/gateway-runtime:1.0.0 +policies: + - name: set-headers + gomodule: github.com/wso2/gateway-controllers/policies/set-headers@v1 +``` + +Each key is resolved independently, so you can override only the ones you need; any key left out falls back to its default public GHCR image. The resolved images are printed at the start of the build under **Resolved images**, so you can confirm which ones were used. + +!!! note "WSO2 Subscription Users" + If you have a WSO2 subscription, use image tags that include the **U2 update version (4th digit)**, for example `1.1.0.0`, instead of the base release `1.1.0`. The 4th digit represents **patch-level (U2) updates**, which include the latest fixes and security updates delivered through the WSO2 private registry (`registry.wso2.com`). + + Because these images are not published to the public GHCR registry, set **all three** keys explicitly to the `registry.wso2.com/wso2-api-platform/` path: + + ```yaml + version: v1 + gateway: + version: "1.1.0.0" + images: + builder: registry.wso2.com/wso2-api-platform/gateway-builder:1.1.0.0 + controller: registry.wso2.com/wso2-api-platform/gateway-controller:1.1.0.0 + runtime: registry.wso2.com/wso2-api-platform/gateway-runtime:1.1.0.0 + policies: + - name: set-headers + gomodule: github.com/wso2/gateway-controllers/policies/set-headers@v1 + ``` + + The `ap` CLI uses your local Docker installation to pull and build images, so authenticate to the private registry first: + + ```bash + docker login registry.wso2.com # use your WSO2 email and access token + ``` + +## Build the Gateway Image + +Once `build.yaml` is ready, run the following command from the directory containing `build.yaml` to build the custom gateway image: + +```bash +ap gateway image build +``` + +This packages the gateway runtime together with all listed policies into a container image that can be used in place of the standard gateway image. + +Once the build completes, the output lists the two image names produced. For example: + +```text +✓ Built gateway images with 1 policies: + • ghcr.io/wso2/api-platform/gateway-v1.0.0-gateway-runtime:1.0.0 + • ghcr.io/wso2/api-platform/gateway-v1.0.0-gateway-controller:1.0.0 +``` + +A `build-manifest.yaml` file is also written alongside `build.yaml`, recording the resolved versions of all policies included in the build. + +## Update the Docker Compose File + +After building, update the `image:` fields in your `docker-compose.yaml` to use the newly built images. + +Locate the `gateway-controller` and `gateway-runtime` services and replace their `image:` values with the images from the build output: + +```yaml +services: + gateway-controller: + image: ghcr.io/wso2/api-platform/gateway-v1.0.0-gateway-controller:1.0.0 # (1) + + gateway-runtime: + image: ghcr.io/wso2/api-platform/gateway-v1.0.0-gateway-runtime:1.0.0 # (2) +``` + +1. Replace with the `gateway-controller` image name from your build output. +2. Replace with the `gateway-runtime` image name from your build output. + +Once updated, start the gateway as usual: + +```bash +docker compose up +``` + +## Deploy the API + +The management API uses basic auth. Export the admin credentials `scripts/setup.sh` provisioned (the +username defaults to `admin`; use the password it printed): + +```sh +export ADMIN_USERNAME=admin +export ADMIN_PASSWORD='' +``` + +```sh +curl -X POST http://localhost:9090/api/management/v0.9/rest-apis \ + -u "$ADMIN_USERNAME:$ADMIN_PASSWORD" \ + -H "Content-Type: application/yaml" \ + --data-binary @- <<'EOF' +apiVersion: gateway.api-platform.wso2.com/v1alpha1 +kind: RestApi +metadata: + name: reading-list-api-v1.0 +spec: + displayName: Reading-List-API + version: v1.0 + context: /reading-list/$version + upstream: + main: + url: https://apis.bijira.dev/samples/reading-list-api-service/v1.0 + policies: + - name: set-headers + version: v1 + params: + request: + headers: + - name: x-wso2-apip-gateway-version + value: v1.0.0 + response: + headers: + - name: x-environment + value: development + operations: + - method: GET + path: /books + - method: POST + path: /books + - method: GET + path: /books/{id} + - method: PUT + path: /books/{id} + - method: DELETE + path: /books/{id} +EOF +``` diff --git a/en/docs/api-gateway/1.2.0/policies/custom-policies/writing-a-custom-policy.md b/en/docs/api-gateway/1.2.0/policies/custom-policies/writing-a-custom-policy.md new file mode 100644 index 000000000..86b5543d3 --- /dev/null +++ b/en/docs/api-gateway/1.2.0/policies/custom-policies/writing-a-custom-policy.md @@ -0,0 +1,368 @@ +--- +title: "Write a Custom Gateway Policy" +description: "Write a custom policy for API Platform Gateway using the Go SDK: implement request and response hooks in the policy chain." +canonical_url: https://wso2.com/api-platform/docs/api-gateway/policies/custom-policies/writing-a-custom-policy/ +md_url: https://wso2.com/api-platform/docs/api-gateway/policies/custom-policies/writing-a-custom-policy.md +tags: + - api-gateway + - policies + - custom-policies +author: WSO2 API Platform Documentation Team +last_updated: 2026-06-17 +content_type: "how-to" +--- + +# Writing a Custom Policy + +This guide shows how to write a custom policy in API Platform Gateway. + +For full API details, see the [API Platform Gateway's Custom Policy SDK Documentation](https://pkg.go.dev/github.com/wso2/api-platform/sdk/core/policy/v1alpha2). + +## How It Works + +Every request and response that flows through the gateway passes through a **policy chain**. Each policy in the chain declares which phases it participates in, and the kernel calls the appropriate hook for each phase: + + +!!! note + A **policy chain** is an ordered sequence of policies that the gateway runs on every request and response for a given API or API operation. Policies execute in the order they are listed in the API definition YAML — each policy sees the modifications made by the ones before it. + +```text +Incoming Request + │ + ▼ + Request Headers ──► OnRequestHeaders() + │ + ▼ + Request Body ──► OnRequestBody() (or OnRequestBodyChunk() for streaming) + │ + ▼ + Upstream + │ + ▼ + Response Headers ──► OnResponseHeaders() + │ + ▼ + Response Body ──► OnResponseBody() (or OnResponseBodyChunk() for streaming) + │ + ▼ + Downstream Client +``` + +A policy only participates in the phases it implements. For example, a policy that only inspects request headers does not implement the response interfaces. + +## How to Write a Policy + +### Step 1: Create the Policy + +Each policy lives in its own Go module. Create a "policies" directory inside your gateway: + +```text +/policies/my-policy/ + ├── go.mod + ├── my_policy.go + └── policy-definition.yaml +``` + +### Step 2: Implement the Base Policy Interface + +```go +package mypolicy + +import ( + "context" + + policy "github.com/wso2/api-platform/sdk/core/policy/v1alpha2" +) + +type MyPolicy struct { + // your config fields +} + +// Mode declares which phases this policy participates in and how bodies +// are handled. The kernel reads this once at startup — there is no +// per-request overhead. +func (p *MyPolicy) Mode() policy.ProcessingMode { + return policy.ProcessingMode{ + RequestHeaderMode: policy.HeaderModeProcess, + RequestBodyMode: policy.BodyModeSkip, + ResponseHeaderMode: policy.HeaderModeSkip, + ResponseBodyMode: policy.BodyModeSkip, + } +} +``` + +#### How to Choose Modes + +| Setting | When to Use | +|---------|-------------| +| `policy.HeaderModeProcess`, `policy.BodyModeBuffer`, or `policy.BodyModeStream` | When your policy needs to inspect or modify that part of the request/response | +| `policy.HeaderModeSkip` or `policy.BodyModeSkip` | When your policy does not need to run in that phase | + +!!! tip + If you do not want your policy to process a specific phase, explicitly set it to `Skip`. + +### Step 3: Implement Phase Interfaces + +Implement only the interfaces for phases you declared in `Mode()` in step 2. + +#### Request Flow + +**Request Header Phase** + +Called before the request body is read. Ideal for authentication, routing decisions, and adding/removing headers. + +```go +func (p *MyPolicy) OnRequestHeaders( + ctx context.Context, + reqCtx *policy.RequestHeaderContext, + params map[string]interface{}, +) policy.RequestHeaderAction { + token := reqCtx.Headers.Get("Authorization") + if len(token) == 0 { + return &policy.ImmediateResponse{ + StatusCode: 401, + Body: []byte(`{"error":"missing authorization"}`), + } + } + return policy.UpstreamRequestHeaderModifications{} // pass through +} +``` + +**Request Body Phase** + +`OnRequestBody` is called only when the request body is fully buffered. + +```go +func (p *MyPolicy) OnRequestBody( + ctx context.Context, + reqCtx *policy.RequestContext, + params map[string]interface{}, +) policy.RequestAction { + // Inspect or modify the request body + return &policy.UpstreamRequestModifications{ + Body: modifiedBody, + } +} +``` + +!!! note + Even if your policy is designed for streaming, you must still implement `OnRequestBody`. This acts as a fallback when the policy chain does not run in streaming mode. + +#### Response Flow + +**Response Header Phase** + +Called after the upstream responds but before the response body is read. + +```go +func (p *MyPolicy) OnResponseHeaders( + ctx context.Context, + respCtx *policy.ResponseHeaderContext, + params map[string]interface{}, +) policy.ResponseHeaderAction { + return &policy.DownstreamResponseHeaderModifications{ + HeadersToSet: map[string]string{ + "X-Policy-Applied": "true", + }, + } +} +``` + +**Response Body Phase** + +`OnResponseBody` is called only when the response body is fully buffered. + +```go +func (p *MyPolicy) OnResponseBody( + ctx context.Context, + respCtx *policy.ResponseContext, + params map[string]interface{}, +) policy.ResponseAction { + if isInvalidContent(respCtx.ResponseBody.Content) { + return &policy.ImmediateResponse{ + StatusCode: 403, + Body: []byte(`{"error":"policy violation"}`), + } + } + return policy.DownstreamResponseModifications{} +} +``` + +!!! note + Even if your policy is designed for streaming, you must still implement `OnResponseBody`. This acts as a fallback when the policy chain does not run in streaming mode. + +### Step 4: Enable Streaming + +Use streaming when processing SSE (Server-Sent Events) responses or large chunked transfers where you cannot or should not buffer the full body. Set `ResponseBodyMode: policy.BodyModeStream` (and/or `RequestBodyMode: policy.BodyModeStream`) in your `Mode()`, then implement the streaming interfaces. + +#### Streaming Request + +Implement `StreamingRequestPolicy` to process request chunks: + +```go +func (p *MyPolicy) Mode() policy.ProcessingMode { + return policy.ProcessingMode{ + RequestHeaderMode: policy.HeaderModeProcess, + RequestBodyMode: policy.BodyModeStream, + } +} + +func (p *MyPolicy) NeedsMoreRequestData(accumulated []byte) bool { + return false // forward every chunk immediately +} + +func (p *MyPolicy) OnRequestBodyChunk( + ctx context.Context, + reqCtx *policy.RequestStreamContext, + chunk *policy.StreamBody, + params map[string]interface{}, +) policy.StreamingRequestAction { + + return &policy.ForwardRequestChunk{ + Body: chunk.Chunk, // nil = pass through + } +} + +// Fallback for non-streaming chains +func (p *MyPolicy) OnRequestBody( + ctx context.Context, + reqCtx *policy.RequestContext, + params map[string]interface{}, +) policy.RequestAction { + return nil +} +``` + +#### Streaming Response + +Implement `StreamingResponsePolicy` to process responses chunk by chunk: + +```go +func (p *MyPolicy) Mode() policy.ProcessingMode { + return policy.ProcessingMode{ + RequestHeaderMode: policy.HeaderModeProcess, + ResponseBodyMode: policy.BodyModeStream, + } +} + +// NeedsMoreResponseData controls when to start forwarding chunks. +// Return true to keep buffering; false to start forwarding immediately. +// The kernel calls this before each chunk is sent downstream. +func (p *MyPolicy) NeedsMoreResponseData(accumulated []byte) bool { + // Buffer until we have at least 512 bytes to inspect + return len(accumulated) < 512 +} + +// OnResponseBodyChunk is called for each chunk of the response body. +func (p *MyPolicy) OnResponseBodyChunk( + ctx context.Context, + respCtx *policy.ResponseStreamContext, + chunk *policy.StreamBody, + params map[string]interface{}, +) policy.StreamingResponseAction { + + // Inspect the chunk content + if containsViolation(chunk.Chunk) { + // Terminate the stream with a final error frame + return &policy.TerminateResponseChunk{ + Body: []byte("data: {\"error\": \"content violation\"}\n\n"), + } + } + + // Forward the chunk as-is + return &policy.ForwardResponseChunk{ + Body: chunk.Chunk, // nil = pass through unchanged + } +} + +// Fallback for non-streaming chains: required by StreamingResponsePolicy +func (p *MyPolicy) OnResponseBody( + ctx context.Context, + respCtx *policy.ResponseContext, + params map[string]interface{}, +) policy.ResponseAction { + return nil +} +``` + +#### Gate-then-Stream Pattern + +A common pattern for guardrail policies is to accumulate chunks until you have enough data to make a decision, then switch to pass-through: + +```go +// Buffer until we can check for a violation, then stream freely +func (p *MyPolicy) NeedsMoreResponseData(accumulated []byte) bool { + // Keep buffering until we have a complete first SSE event + return !bytes.Contains(accumulated, []byte("\n\n")) +} +``` + +### Step 5: Factory Function + +Initialize your policy and validate parameters: + +```go +func GetPolicy( + metadata policy.PolicyMetadata, + params map[string]interface{}, +) (policy.Policy, error) { + + maxSize, ok := params["maxBodySize"].(float64) + if !ok { + return nil, fmt.Errorf("invalid maxBodySize") + } + + return &MyPolicy{maxBodySize: int(maxSize)}, nil +} +``` + +### Step 6: Define Parameters + +Create a `policy-definition.yaml` in your policy directory: + +```yaml +name: my-policy +displayName: my policy +version: v1.0.0 + +parameters: + type: object + properties: + maxBodySize: + type: integer + default: 1048576 +``` + +### Step 7: Share Data Between Phases + +Use the `Metadata` map to pass data between request and response phases: + +```go +// In request phase +reqCtx.Metadata["clientID"] = clientID + +// In response phase +clientID := respCtx.Metadata["clientID"] +``` + +### Step 8: Register and Build + +Add your policy to gateway folder's `build.yaml` under `policies:` using `filePath` for local development: + +```yaml +policies: + - name: my-policy + filePath: ./policies/my-policy +``` + +For published policies (production), use the module reference instead: + +```yaml +policies: + - name: my-policy + gomodule: github.com/abc/policy-repo/policies/my-policy@v1 +``` + +## What's Next? + +- [Building the Gateway with Custom Policies](building-gateway-with-custom-policies.md): Build a gateway image that includes your custom policy diff --git a/en/docs/api-gateway/1.2.0/policies/overview.md b/en/docs/api-gateway/1.2.0/policies/overview.md new file mode 100644 index 000000000..8bb57d504 --- /dev/null +++ b/en/docs/api-gateway/1.2.0/policies/overview.md @@ -0,0 +1,266 @@ +--- +title: "API Platform Policies Overview" +description: "Understand gateway policies in WSO2 API Platform: how Go and Python policies execute in the request and response pipeline and how to chain them." +canonical_url: https://wso2.com/api-platform/docs/api-gateway/policies/overview/ +md_url: https://wso2.com/api-platform/docs/api-gateway/policies/overview.md +tags: + - api-gateway + - policies + - extensibility +author: WSO2 API Platform Documentation Team +last_updated: 2026-06-11 +content_type: "concept" +--- + +# API Platform Policies Overview + +## What is a Policy? + +A policy is a pluggable unit of behavior that runs in the gateway request or response pipeline. Policies can be applied at the API level (all operations) or at individual operation level, and can run on requests, responses, or both. + +Policies handle cross-cutting concerns such as authentication, rate limiting, header manipulation, payload transformation, content moderation, and LLM-specific controls (prompt decoration, semantic caching, token limits, guardrails). Multiple policies can be chained together on the same API or operation. + +Each built-in policy is versioned independently. When a new version is published, older versions remain available so existing deployments are not affected. + +You can also write your own policy and then use API Platform CLI to build a custom gateway image that includes any combination of built-in policies and your own implementations. For more information, see [Custom Policies](custom-policies/writing-a-custom-policy.md). + +## Policy Languages and Runtimes + +The gateway supports **two languages** for authoring policies: + +| Language | Runtime | Best For | +|----------|---------|----------| +| **Go** (default) | Compiled into the Policy Engine binary | Standard API policies — authentication, rate limiting, header manipulation, guardrails | +| **Python** (beta) | Executed by the Python Executor | AI/ML workloads, prompt engineering, complex data transformations, and scenarios that benefit from Python's rich ecosystem | + +Go is the **primary and recommended language** for policy development. It provides maximum execution performance, strict type safety, and minimal per-request latency. Python is available as a **specialized runtime** for use cases where access to Python-native libraries (NLP toolkits, compression engines, ML inference clients, etc.) outweighs the overhead of cross-process communication. + +## How Policies Execute + +Understanding where each language fits requires a brief look at the Gateway Runtime architecture: + +``` + ┌──────────────────────────────────────┐ + │ Gateway Runtime │ + Incoming │ │ + Request ────────► │ ┌────────┐ ┌──────────────┐ │ Upstream + │ │ Router │─────►│ Policy Engine│────┼────► Backend + Response ◄──────── │ │(Envoy) │◄─────│ (Go) │ │ + │ └────────┘ └──────┬───────┘ │ + │ │ gRPC/UDS │ + │ ┌──────▼───────┐ │ + │ │ Python │ │ + │ │ Executor │ │ + │ └──────────────┘ │ + └──────────────────────────────────────┘ +``` + +- **Go policies** are compiled directly into the **Policy Engine** binary at image build time. When the Router hands off a request to the Policy Engine via the `ext_proc` filter, Go policies execute in-process with zero serialization overhead. + +- **Python policies** run in a dedicated **Python Executor** process. The Go Policy Engine delegates execution to the Python Executor over a local gRPC connection using a Unix Domain Socket. The executor manages policy lifecycle — loading, initialization, execution, and teardown in an isolated Python runtime. + +!!! note + Both Go and Python policies share the same policy evaluation pipeline. From the perspective of API configuration and deployment, a policy's language is transparent — you attach Go and Python policies to APIs in exactly the same way. + +## Policy Anatomy + +Regardless of language, every policy consists of two parts: + +* Policy definition +* Policy implementation + +### Policy Definition (`policy-definition.yaml`) + +A declarative YAML file that describes the policy's identity, version, and configuration schema. This file is the same for both Go and Python policies. + +```yaml +name: my-policy +version: v1.0.0 +displayName: My Policy +description: | + A short description of what this policy does. + +parameters: + type: object + properties: + myParam: + type: string + description: "An example parameter." + default: "hello" + required: + - myParam + +systemParameters: + type: object + additionalProperties: false + properties: {} +``` + +| Field | Purpose | +|-------|---------| +| `name` | Unique policy identifier, used in API definitions to reference the policy | +| `version` | Semantic version (e.g., `v1.0.0`). The major version is used as the policy version qualifier | +| `parameters` | JSON Schema describing the user-configurable parameters for the policy | +| `systemParameters` | JSON Schema for operator-level configuration (set via gateway config, not per-API) | + +### Policy Implementation + +The implementation is where the two languages diverge. + +### Go Policies + +Go is the **default and recommended** language for policy development. Every built-in policy that ships with the gateway — authentication, rate limiting, CORS, guardrails, header manipulation — is written in Go. + +#### Why Go? + +- **Performance:** Compiled into the Policy Engine binary. No serialization, no IPC, no interpreter overhead. +- **Type safety:** Compile-time guarantees reduce runtime errors in production. +- **Ecosystem alignment:** The Policy Engine, Gateway Builder, and Gateway Controller are all Go codebases. +- **Broad applicability:** Ideal for the vast majority of API management use cases. + +#### Go Policy Structure + +A Go policy is a standard **Go module** containing the policy definition and the implementation: + +``` +my-go-policy/ +├── policy-definition.yaml +├── go.mod +├── go.sum +├── policy.go +└── policy_test.go +``` + +| File | Purpose | +|------|---------| +| `policy-definition.yaml` | Declares name, version, and parameter schema | +| `go.mod` / `go.sum` | Go module definition and dependency lockfiles | +| `policy.go` | Policy implementation | +| `policy_test.go` | Unit tests for the policy logic | + +Go policies implement interfaces from the gateway's Policy SDK. The Policy Engine loads them at build time via the `build.yaml` manifest. + +#### Build Integration + +Go policies are referenced in `build.yaml` using the `gomodule` field, which points to the Go module path: + +```yaml +policies: + - name: my-go-policy + gomodule: github.com/wso2/gateway-controllers/policies/my-go-policy@v1 +``` + +The **Gateway Builder** resolves these modules, compiles them into the Policy Engine binary, and produces a custom gateway image containing all declared policies. + +### Python Policies (Beta) + +Python policy support extends the gateway's capabilities into domains where Python's ecosystem is unmatched — particularly **AI/ML, natural language processing, and complex data transformations**. + +#### Why Python? + +- **AI/ML ecosystem:** Direct access to libraries like `transformers`, `tiktoken`, `scikit-learn`, and custom compression engines. +- **Rapid prototyping:** Faster iteration for experimental or research-oriented policies. +- **Specialized use cases:** Prompt compression, semantic analysis, content classification, and other tasks where Python libraries provide capabilities that would be impractical to reimplement in Go. + +#### Python Policy Structure + +Python policies follow the standard `src` layout and are packaged as installable Python packages: + +``` +my-python-policy/ +├── policy-definition.yaml +├── pyproject.toml +├── requirements.txt +├── src/ +│ └── my_python_policy_v1/ +│ ├── __init__.py +│ └── policy.py +└── tests/ + └── test_policy.py +``` + +| File | Purpose | +|------|---------| +| `policy-definition.yaml` | Same format as Go — declares name, version, and parameter schema | +| `pyproject.toml` | Standard Python packaging configuration. Uses `hatchling` as the build backend | +| `requirements.txt` | Runtime dependencies | +| `src//policy.py` | Policy implementation | +| `tests/` | Unit tests for the policy logic | + +#### Build Integration + +Python policies are referenced in `build.yaml` using the `pipPackage` field instead of `gomodule`: + +```yaml +policies: + - name: my-python-policy + pipPackage: github.com/wso2/gateway-controllers/policies/my-python-policy@v1 +``` + +The Gateway Builder resolves the Python package, installs its dependencies, generates the policy registry, and bundles everything into the gateway image alongside the Python Executor. + +## Choosing a Language for Policy + +Use this decision guide when planning a new policy: + +| Consideration | Choose Go | Choose Python | +|---------------|-----------|---------------| +| **Performance-critical path** | ✅ In-process, zero overhead | ❌ Cross-process gRPC call | +| **Standard API management** (auth, rate limiting, headers) | ✅ Existing patterns and SDK | Possible, but unnecessary | +| **AI/ML or NLP processing** | Requires reimplementation of libraries | ✅ Direct access to Python ecosystem | +| **Complex data transformations** | Good for structured transforms | ✅ Better for text/NLP transforms | +| **Third-party library dependency** | Go library must exist | ✅ Vast PyPI ecosystem | +| **Production stability** | ✅ Compiled, type-safe | Interpreted, requires thorough testing | +| **Team expertise** | Go-proficient team | Python-proficient team | + +Start with Go unless your policy specifically requires Python libraries or Python-native capabilities. The majority of gateway policies are written in Go. + +## Available Policies + +The following table presents the available policies alphabetically. + + +| Policy | Categories | Description | +|--------|------------|-------------| +| [Analytics Header Filter](https://github.com/wso2/gateway-controllers/blob/main/docs/analytics-header-filter/v1.0/docs/analytics-header-filter.md) | Logging, Analytics & Monitoring | The Analytics Header Filter policy allows you to control which request and response headers are included in analytics data using allow or deny modes. | +| [API Key Auth](https://github.com/wso2/gateway-controllers/blob/main/docs/api-key-auth/v1.0/docs/apikey-authentication.md) | Security, AI | Implements API Key Authentication to protect APIs with pre-shared API keys. | +| [AWS Bedrock Guardrail](https://github.com/wso2/gateway-controllers/blob/main/docs/aws-bedrock-guardrail/v1.0/docs/aws-bedrock-guardrail.md) | Guardrails, AI | Validates request or response body content against AWS Bedrock Guardrails. | +| [Azure Content Safety Content Moderation](https://github.com/wso2/gateway-controllers/blob/main/docs/azure-content-safety-content-moderation/v1.0/docs/azure-content-safety.md) | Guardrails, AI | Validates request or response body content against Azure Content Safety API for content moderation. | +| [Basic Auth](https://github.com/wso2/gateway-controllers/blob/main/docs/basic-auth/v1.0/docs/basic-auth.md) | Security, AI | Implements HTTP Basic Authentication to protect APIs with username and password credentials. | +| [Content Length Guardrail](https://github.com/wso2/gateway-controllers/blob/main/docs/content-length-guardrail/v1.0/docs/content-length.md) | Guardrails, AI | Validates the byte length of request or response body content. | +| [CORS](https://github.com/wso2/gateway-controllers/blob/main/docs/cors/v1.0/docs/cors.md) | Security, AI, MCP | Cross-Origin Resource Sharing (CORS) policy that handles preflight requests and adds appropriate CORS headers to responses. | +| [Dynamic Endpoint](https://github.com/wso2/gateway-controllers/blob/main/docs/dynamic-endpoint/v1.0/docs/dynamic-endpoint.md) | Transformation | Routes requests to a named upstream definition at request time. | +| [Host Rewrite](https://github.com/wso2/gateway-controllers/blob/main/docs/host-rewrite/v1.0/docs/host-rewrite.md) | Transformation | Sets the Host/:authority header sent to the upstream. | +| [Interceptor Service](https://github.com/wso2/gateway-controllers/blob/main/docs/interceptor-service/v1.0/docs/interceptor-service.md) | Transformation | Invokes a user-defined HTTP interceptor service in the request and/or response phase. | +| [JSON Schema Guardrail](https://github.com/wso2/gateway-controllers/blob/main/docs/json-schema-guardrail/v1.0/docs/json-schema.md) | Guardrails, AI | Validates request or response body content against a JSON Schema. | +| [JSON/XML Mediator](https://github.com/wso2/gateway-controllers/blob/main/docs/json-xml-mediator/v1.0/docs/json-xml-mediator.md) | Transformation | Mediates request and response payloads between downstream and upstream JSON/XML formats. | +| [JWT Auth](https://github.com/wso2/gateway-controllers/blob/main/docs/jwt-auth/v1.0/docs/jwt-authentication.md) | Security, AI | Validates JWT access tokens using one or more JWKS providers (key managers). | +| [LLM Cost](https://github.com/wso2/gateway-controllers/blob/main/docs/llm-cost/v1.0/docs/llm-cost.md) | AI | Calculates the monetary cost of LLM API calls at response time and stores the result in SharedContext for use by downstream policies. | +| [LLM Cost Based Ratelimit](https://github.com/wso2/gateway-controllers/blob/main/docs/llm-cost-based-ratelimit/v1.0/docs/llm-cost-based-ratelimit.md) | AI | A specialized rate limiting policy that enforces monetary budget limits on LLM API usage. | +| [Log Message](https://github.com/wso2/gateway-controllers/blob/main/docs/log-message/v1.0/docs/log-message.md) | Logging, Analytics & Monitoring, MCP | This policy provides the capability to log the payload and headers of a request/response. | +| [MCP Access Control](https://github.com/wso2/gateway-controllers/blob/main/docs/mcp-acl-list/v1.0/docs/mcp-acl-list.md) | MCP, AI | MCP ACL List policy controls access to tools, resources, and prompts using mode and exceptions. | +| [MCP Authentication](https://github.com/wso2/gateway-controllers/blob/main/docs/mcp-auth/v1.0/docs/mcp-authentication.md) | MCP, AI, Security | This policy is used to secure traffic to Model Context Protocol server as defined in the specification (https://modelcontextprotocol.io/specification/2025-06-18/basic/authorization). | +| [MCP Authorization](https://github.com/wso2/gateway-controllers/blob/main/docs/mcp-authz/v1.0/docs/mcp-authorization.md) | MCP, AI, Security | MCP Authorization policy validates access to MCP resources (tools, resources, prompts) and methods based on JWT claims or OAuth scopes provided by the mcp-auth policy. | +| [MCP Rewrite](https://github.com/wso2/gateway-controllers/blob/main/docs/mcp-rewrite/v1.0/docs/mcp-rewrite.md) | MCP, AI | MCP Rewrite policy defines user-facing tools, resources, and prompts and maps them to backend capability names using optional "target" fields. | +| [Model Round Robin](https://github.com/wso2/gateway-controllers/blob/main/docs/model-round-robin/v1.0/docs/model-round-robin.md) | AI | Implements round-robin load balancing for AI models. | +| [Model Weighted Round Robin](https://github.com/wso2/gateway-controllers/blob/main/docs/model-weighted-round-robin/v1.0/docs/model-weighted-round-robin.md) | AI | Implements weighted round-robin load balancing for AI models. | +| [PII Masking Regex](https://github.com/wso2/gateway-controllers/blob/main/docs/pii-masking-regex/v1.0/docs/pii-masking-regex.md) | Guardrails, AI | Masks or redacts Personally Identifiable Information (PII) from request/response bodies using regex patterns. | +| [Prompt Compressor](https://github.com/wso2/gateway-controllers/blob/main/docs/prompt-compressor/v0.9/docs/prompt-compressor.md) | AI | Compresses selected prompt text in JSON request bodies before upstream LLM calls. | +| [Prompt Decorator](https://github.com/wso2/gateway-controllers/blob/main/docs/prompt-decorator/v1.0/docs/prompt-decorator.md) | AI | Dynamically modifies the prompt by applying custom decorations using a configured strategy. | +| [Prompt Template](https://github.com/wso2/gateway-controllers/blob/main/docs/prompt-template/v1.0/docs/prompt-template.md) | AI | Dynamically modifies the prompt by applying custom templates using a configured strategy. | +| [Rate Limit - Advanced](https://github.com/wso2/gateway-controllers/blob/main/docs/advanced-ratelimit/v1.0/docs/advanced-ratelimit.md) | Security, AI | Rate limiting policy supporting multiple algorithms (GCRA, Fixed Window), multi-dimensional quotas, weighted rate limiting, flexible key extraction, and both in-memory and Redis backends. | +| [Rate Limit - Basic](https://github.com/wso2/gateway-controllers/blob/main/docs/basic-ratelimit/v1.0/docs/basic-ratelimit.md) | Security, AI | Simple request rate limiting policy that limits the number of requests per time window. | +| [Regex Guardrail](https://github.com/wso2/gateway-controllers/blob/main/docs/regex-guardrail/v1.0/docs/regex.md) | Guardrails, AI | Validates request or response body content against a regular expression pattern. | +| [Remove Headers](https://github.com/wso2/gateway-controllers/blob/main/docs/remove-headers/v1.0/docs/remove-headers.md) | Transformation, MCP | This policy provides the capability to remove headers from either the request or the response. | +| [Request Rewrite](https://github.com/wso2/gateway-controllers/blob/main/docs/request-rewrite/v1.0/docs/request-rewrite.md) | Transformation | Rewrites incoming requests by updating path, query parameters, and/or HTTP method before forwarding to upstream services. | +| [Respond](https://github.com/wso2/gateway-controllers/blob/main/docs/respond/v1.0/docs/respond.md) | AI | Returns an immediate response to the client without forwarding the request to the upstream backend. | +| [Semantic Cache](https://github.com/wso2/gateway-controllers/blob/main/docs/semantic-cache/v1.0/docs/semantic-caching.md) | AI | Implements semantic caching for LLM responses using vector similarity search. | +| [Semantic Prompt Guard](https://github.com/wso2/gateway-controllers/blob/main/docs/semantic-prompt-guard/v1.0/docs/semantic-prompt-guard.md) | Guardrails, AI | Blocks or allows prompts based on semantic similarity to configured allow/deny phrase embeddings. | +| [Semantic Tool Filtering](https://github.com/wso2/gateway-controllers/blob/main/docs/semantic-tool-filtering/v1.0/docs/semantic-tool-filtering.md) | Guardrails, AI | Dynamically filters the tools provided within an API request based on their semantic relevance to the user query. | +| [Sentence Count Guardrail](https://github.com/wso2/gateway-controllers/blob/main/docs/sentence-count-guardrail/v1.0/docs/sentence-count.md) | Guardrails, AI | Validates the sentence count of request or response body content. | +| [Set Headers](https://github.com/wso2/gateway-controllers/blob/main/docs/set-headers/v1.0/docs/set-headers.md) | Transformation, MCP | This policy provides the capability to set arbitrary headers to either the request or the response. | +| [Subscription Validation](https://github.com/wso2/gateway-controllers/blob/main/docs/subscription-validation/v1.0/docs/subscription-validation.md) | Security | Validates that incoming requests are associated with an active subscription for the target API. | +| [Token Based Ratelimit](https://github.com/wso2/gateway-controllers/blob/main/docs/token-based-ratelimit/v1.0/docs/token-based-ratelimit.md) | AI | A specialized rate limiting policy for LLM APIs that enforces usage quotas based on token counts. | +| [URL Guardrail](https://github.com/wso2/gateway-controllers/blob/main/docs/url-guardrail/v1.0/docs/url.md) | Guardrails, AI | Validates URLs found in request or response body content. | +| [Word Count Guardrail](https://github.com/wso2/gateway-controllers/blob/main/docs/word-count-guardrail/v1.0/docs/word-count.md) | Guardrails, AI | Validates the word count of request or response body content. | + diff --git a/en/docs/api-gateway/1.2.0/quick-start-guide.md b/en/docs/api-gateway/1.2.0/quick-start-guide.md new file mode 100644 index 000000000..a5a47f73b --- /dev/null +++ b/en/docs/api-gateway/1.2.0/quick-start-guide.md @@ -0,0 +1,167 @@ +--- +title: "API Platform Gateway Quick Start Guide" +description: "Run API Platform Gateway with Docker Compose, deploy your first REST API configuration, and invoke it through the gateway in minutes." +canonical_url: https://wso2.com/api-platform/docs/api-gateway/quick-start-guide/ +md_url: https://wso2.com/api-platform/docs/api-gateway/quick-start-guide.md +tags: + - api-gateway + - quickstart + - docker +author: WSO2 API Platform Documentation Team +last_updated: 2026-08-05 +content_type: "quickstart" +--- + +# Quick Start Guide + +### Using Docker Compose (Recommended) + +### Prerequisites + +A Docker-compatible container runtime such as: + +- Docker Desktop (Windows / macOS) +- Podman Desktop or Podman (Windows / macOS / Linux) +- Rancher Desktop (Windows / macOS) +- Colima (macOS) +- Docker Engine + Compose plugin (Linux) + +These examples use `docker compose`. If you use another Compose-compatible runtime, use the equivalent commands. + +Verify the commands for your runtime are available. For Docker: + +```bash +docker --version +docker compose version +``` + + + +```bash +# Download distribution. +wget https://github.com/wso2/api-platform/releases/download/gateway/v1.2.0-rc2/wso2apip-api-gateway-1.2.0-rc2.zip + +# Unzip the downloaded distribution. +unzip wso2apip-api-gateway-1.2.0-rc2.zip + +cd wso2apip-api-gateway-1.2.0/ + +# Run the one-time setup. This provisions the AES-256 at-rest encryption key, +# the router HTTPS listener certificate, api-platform.env, and the gateway-controller +# admin credentials. It prints the admin password once — copy it. +./scripts/setup.sh + +# Export the admin credentials so the management-API calls below can authenticate. +# The username defaults to "admin"; use the password setup.sh just printed. +export ADMIN_USERNAME=admin +export ADMIN_PASSWORD='' + +# Start the complete stack +docker compose up + +# Verify gateway controller admin endpoint is running +curl http://localhost:9094/api/admin/v1/health + +# Deploy an API configuration +curl -X POST http://localhost:9090/api/management/v1/rest-apis \ + -u "$ADMIN_USERNAME:$ADMIN_PASSWORD" \ + -H "Content-Type: application/yaml" \ + --data-binary @- <<'EOF' +apiVersion: gateway.api-platform.wso2.com/v1 +kind: RestApi +metadata: + name: reading-list-api-v1.0 +spec: + displayName: Reading-List-API + version: v1.0 + context: /reading-list/$version + upstream: + main: + url: https://apis.bijira.dev/samples/reading-list-api-service/v1.0 + policies: + - name: set-headers + version: v1 + params: + request: + headers: + - name: x-wso2-apip-gateway-version + value: v1.0.0 + response: + headers: + - name: x-environment + value: development + operations: + - method: GET + path: /books + - method: POST + path: /books + - method: GET + path: /books/{id} + - method: PUT + path: /books/{id} + - method: DELETE + path: /books/{id} +EOF + + +# Test routing through the gateway +curl -i http://localhost:8080/reading-list/v1.0/books +curl -ik https://localhost:8443/reading-list/v1.0/books +``` + +!!! tip "Port 8080, 8443, 9090, or 9094 already taken?" + If the start command fails with a port binding error, identify what is already listening on the default ports: + + On macOS or Linux, run: + + ```bash + lsof -nP -iTCP:8080 -sTCP:LISTEN + lsof -nP -iTCP:8443 -sTCP:LISTEN + lsof -nP -iTCP:9090 -sTCP:LISTEN + lsof -nP -iTCP:9094 -sTCP:LISTEN + ``` + + On Windows PowerShell, run: + + ```powershell + Get-NetTCPConnection -State Listen -LocalPort 8080,8443,9090,9094 | Select-Object LocalAddress, LocalPort, OwningProcess + ``` + + Stop the conflicting service if you don't need it. If you need to keep it running, change the host-side value of the relevant `ports:` mapping in `docker-compose.yaml`. Then use the remapped host port in the verification and test commands on this page. + +!!! note "Running on Windows" + The commands above assume a Linux/macOS shell. On Windows, run the one-time setup with the PowerShell script instead — it takes the same flags and provisions the same files: + + ```powershell + powershell -ExecutionPolicy Bypass -File .\scripts\setup.ps1 + ``` + + Then set the admin credentials with `$env:ADMIN_USERNAME='admin'` and `$env:ADMIN_PASSWORD=''` in place of the `export` lines. + + The `curl` command that deploys the API pipes its YAML payload in through a shell heredoc (`--data-binary @- <<'EOF'`), which PowerShell does not support. Either run it from Git Bash or WSL, or save the YAML between the `EOF` markers to a file and post that file explicitly — note the `.exe`, since `curl` is an alias for `Invoke-WebRequest` in Windows PowerShell: + + ```powershell + curl.exe -X POST http://localhost:9090/api/management/v1/rest-apis ` + -u "${env:ADMIN_USERNAME}:${env:ADMIN_PASSWORD}" ` + -H "Content-Type: application/yaml" ` + --data-binary "@reading-list-api.yaml" + ``` + +!!! tip "Customizing configuration" + The setup script (`setup.sh`, or `setup.ps1` on Windows) writes `api-platform.env`, which is loaded into the containers via Docker Compose `env_file`. To change the storage backend, connect to a control plane, or tune other settings, edit that file (or the `config.toml` interpolation tokens directly). See [Gateway Configuration and Environment Interpolation](./setup/configuration.md). + +### Stopping the Gateway + +When stopping the gateway, you have two options: + +**Option 1: Stop runtime, keep data (persisted APIs and configuration)** +```bash +docker compose down +``` +This stops the containers but preserves the `controller-data` volume. When you restart with `docker compose up`, all your API configurations will be restored. + +**Option 2: Complete shutdown with data cleanup (fresh start)** +```bash +docker compose down -v +``` +This stops containers and removes the `controller-data` volume. Next startup will be a clean slate with no persisted APIs or configuration. \ No newline at end of file diff --git a/en/docs/api-gateway/1.2.0/resiliency/timeouts.md b/en/docs/api-gateway/1.2.0/resiliency/timeouts.md new file mode 100644 index 000000000..be7a9cf91 --- /dev/null +++ b/en/docs/api-gateway/1.2.0/resiliency/timeouts.md @@ -0,0 +1,269 @@ +--- +title: "Configure Timeouts" +description: "Configure gateway level and API-level timeouts to protect against slow or unreachable backends and slow clients." +canonical_url: https://wso2.com/api-platform/docs/api-gateway/resiliency/timeouts/ +md_url: https://wso2.com/api-platform/docs/api-gateway/resiliency/timeouts.md +tags: + - api-gateway + - configuration + - timeouts + - resiliency +author: WSO2 API Platform Documentation Team +last_updated: 2026-06-30 +content_type: "how-to" +--- + +# Configuring Timeouts + +This guide explains how to configure **timeouts** for the API Platform Gateway so that requests to slow or unreachable backends, and slow downstream clients, fail within a predictable time instead of hanging indefinitely. + +Timeouts are configured at **two levels**: + +| Level | Where it is configured | Scope | Typical use | +|-------|------------------------|-------|-------------| +| **Gateway level** | Inside the gateway `config.toml` | Global defaults applied to **all** traffic on the gateway | Operator-wide baselines (connect timeout, default route timeout, downstream/HCM timeouts) | +| **API level** | The API definition — the `resilience` block and `upstreamDefinitions[].timeout.connect` | A single API, its routes, and its upstream clusters | Per-API/per-route overrides for backends that are faster or slower than the global default | + +The gateway level establishes the **defaults**. The API level **overrides** those defaults for a specific API (and, for REST APIs, a specific operation). When an API does not specify a value, the gateway-level default applies. + +## Gateway-level timeouts (config.toml) + +These timeouts are global defaults that apply to every request handled by the gateway, regardless of which API serves it. You can use `gateway/configs/config-template.toml` as a reference when creating your own `config.toml`. + +There are two groups: + +- **Upstream timeouts** (`[router.upstream.timeouts]`) — govern the connection to, and the response from, the **backend**. +- **HTTP Connection Manager (HCM) timeouts** (`[router.http_listener.timeouts]`) — govern the **downstream** connection between the client and the gateway. + +### Upstream timeouts + +```toml +[router.upstream.timeouts] +route_timeout_ms = 60000 # Default per-route request→response timeout +route_idle_timeout_ms = 300000 # Default per-route stream idle timeout +connect_timeout_ms = 5000 # TCP connection establishment timeout +``` + +| Setting | Default | Maps to (Envoy) | Description | +|---------|---------|-----------------|---------| +| `connect_timeout_ms` | `5000` | Cluster `connect_timeout` | How long the gateway waits to **establish a TCP connection** to an upstream endpoint before failing the request. | +| `route_timeout_ms` | `60000` | `RouteAction.timeout` | Default maximum time for the **entire request→upstream-response** on a route. This is the default that an API's `resilience.timeout` overrides. | +| `route_idle_timeout_ms` | `300000` | `RouteAction.idle_timeout` | Default **per-route stream idle** timeout. This is the default that an API's `resilience.idleTimeout` overrides. | + +!!! note + All three values must be **positive** (greater than `0`). To disable a *route* timeout for a specific API, set its `resilience` value to `0s` (see [API-level timeouts](#api-level-timeouts)). + +### HTTP Connection Manager (downstream) timeouts + +These govern the connection between the **client and the gateway**. A value of `"0s"` disables the corresponding timeout. + +```toml +# HTTP Connection Manager (downstream) timeouts +[router.http_listener.timeouts] +request_timeout = "0s" # Max duration for the entire downstream request +request_headers_timeout = "0s" # Max duration to receive the complete request headers +stream_idle_timeout = "5m" # Idle timeout for a single HTTP stream/request +idle_timeout = "1h" # Idle timeout for the downstream connection +``` + +| Setting | Default | Maps to (Envoy) | Description | +|---------|---------|-----------------|---------| +| `request_timeout` | `0s` (disabled) | HCM `request_timeout` | Max time for the gateway to receive the **entire** downstream request. | +| `request_headers_timeout` | `0s` (disabled) | HCM `request_headers_timeout` | Max time to receive the **complete request headers**. | +| `stream_idle_timeout` | `5m` | HCM `stream_idle_timeout` | Idle timeout for a single HTTP **stream/request**. | +| `idle_timeout` | `1h` | HCM `common_http_protocol_options.idle_timeout` | Idle timeout for the downstream **connection**. | + +Durations use Go duration syntax with a single unit (for example `"30s"`, `"500ms"`, `"5m"`, `"1h"`). If a value is left unset, the Envoy default for that field applies. + +!!! warning + `idle_timeout` can also be disabled by setting it to zero explicitly. Disabling it has a high likelihood of yielding connection leaks (for example, due to lost TCP FIN packets). + +### Setting gateway-level timeouts in different deployments + +#### Standalone / local + +Configure the timeouts directly in `gateway/configs/config.toml` using the structure shown above. + +#### Kubernetes with Helm + +When deploying the gateway via the Helm chart, the same settings are controlled through Helm values under `gateway.config.router.*`. Example `values.yaml` snippet: + +```yaml +gateway: + config: + router: + upstream: + timeouts: + connect_timeout_ms: 5000 + route_timeout_ms: 60000 + route_idle_timeout_ms: 300000 + http_listener: + timeouts: + request_timeout: "0s" + request_headers_timeout: "0s" + stream_idle_timeout: "5m" + idle_timeout: "1h" +``` + +The chart renders these values into the generated `config.toml` used by the gateway controller. + +## API-level timeouts + +Two of the timeout layers are naturally per-API and can be tuned directly in the API definition, each through its own field: + +| Surface | Field(s) | Maps to (Envoy) | Overrides gateway default | +|---------|----------|-----------------|--------------------------| +| Connect timeout per upstream (`upstreamDefinitions[].timeout.connect`) | `connect` | Cluster `connect_timeout` | `connect_timeout_ms` | +| `resilience` block | `timeout`, `idleTimeout` | `RouteAction.timeout` / `idle_timeout` | `route_timeout_ms` / `route_idle_timeout_ms` | + +The HCM (downstream) timeouts have no per-API equivalent and remain gateway-level only. + +### Connect timeout per upstream (`upstreamDefinitions[].timeout.connect`) + +When an API routes through a named **upstream definition**, that definition can set its own `connect` timeout, which overrides the gateway-level `connect_timeout_ms` for the cluster built from it. Because the setting lives on the upstream definition, different upstreams used by the same API can have different connect timeouts. + +```yaml +apiVersion: gateway.api-platform.wso2.com/v1 +kind: RestApi +metadata: + name: orders-api +spec: + displayName: Orders API + version: v1.0 + context: /orders/$version + upstreamDefinitions: + - name: orders-backend + timeout: + connect: 6000ms # overrides connect_timeout_ms for this cluster + upstreams: + - url: http://orders.internal:8080 + upstream: + main: + ref: orders-backend # this API routes through the orders-backend cluster + operations: + - method: GET + path: /list +``` + +- `connect` is a duration string (for example `6000ms`, `5s`). +- It governs only **TCP connection establishment** (plus the TLS handshake for HTTPS upstreams) — not the response wait. +- An API that uses a direct `upstream.main.url` (instead of an `upstreamDefinitions` ref) uses the gateway-level `connect_timeout_ms`. + +### Timeouts via `resilience` block + +The `resilience` block lets an individual API override the gateway-level default **route** timeouts for its own traffic. It maps to Envoy's `RouteAction` timeouts. + +It supports two fields: + +| Field | Maps to (Envoy) | Overrides gateway default | +|-------|-----------------|--------------------------| +| `timeout` | `RouteAction.timeout` | `route_timeout_ms` | +| `idleTimeout` | `RouteAction.idle_timeout` | `route_idle_timeout_ms` | + +!!! note "Duration format" + `timeout` and `idleTimeout` take single-unit duration strings (for example `30s`, `500ms`, `1.5m`). Use `0s` to disable a timeout, or omit the field to fall back to the gateway-level default. Compound (`1h30m`), negative (`-30s`), and unitless (`0`, `30`) values are rejected. + + +For `RestApi`, the `resilience` block can be set at **two levels**: + +- **API level** — applies to every operation/route of the API. +- **Operation level** — applies to that single operation and overrides the API-level value. + +Precedence is **most-specific-wins, per field**: an operation's `timeout` overrides the API's `timeout`, while still inheriting the API's `idleTimeout` if the operation does not set its own. If neither level sets a field, the gateway-level default applies. + +```yaml +apiVersion: gateway.api-platform.wso2.com/v1 +kind: RestApi +metadata: + name: orders-api +spec: + displayName: Orders API + version: v1.0 + context: /orders/$version + upstream: + main: + url: https://backend.example.com + # API-level: applies to all operations unless overridden. + resilience: + timeout: 15s + idleTimeout: 30s + operations: + - method: GET + path: /list + - method: POST + path: /reports + # Operation-level override: this slow report endpoint gets a longer + # route timeout, but still inherits the API-level idleTimeout (30s). + resilience: + timeout: 60s +``` + +Resolution for the example above: + +| Route | `timeout` | `idleTimeout` | +|-------|-----------|---------------| +| `GET /list` | `15s` (API level) | `30s` (API level) | +| `POST /reports` | `60s` (operation level) | `30s` (inherited from API level) | + + + +## Practical guidance + +### Connect timeout (`connect_timeout_ms`) + +- **Decrease** when backends are highly available and you want to fail fast on unhealthy or misconfigured targets, freeing resources quickly. +- **Increase** when backends sit behind slower networks/load balancers, or may experience cold starts or scaling events that briefly delay connection establishment. +- Avoid setting it too low — it may cause **false-positive timeouts** during short periods of backend slowness or network jitter. +- Set the **gateway default** for the common case, and override **per upstream** via `upstreamDefinitions[].timeout.connect` (see [Connect timeout per upstream](#connect-timeout-per-upstream-upstreamdefinitionstimeoutconnect)) when a specific backend needs a different connection budget. + +### Route timeout (`route_timeout_ms` / `resilience.timeout`) + +- Set the **gateway default** to a sane upper bound for typical backends, and use **API/operation-level** `resilience.timeout` for endpoints that are known to be faster or legitimately slower. +- For streaming/long-lived responses, be mindful of `idleTimeout`: a long total `timeout` with a short `idleTimeout` can still cut a slow token stream. Leave `idleTimeout` generous (or unset) for streaming providers. + +### HCM/downstream timeouts + +- `request_headers_timeout` is a useful protection against slow-header (Slowloris-style) clients; enable it with a small value (for example `"5s"`) if your clients are expected to send headers promptly. +- `request_timeout` is disabled by default; enable it only if you want a hard cap on how long the gateway will spend receiving an entire downstream request. + +## Example scenarios + +### A slow-to-connect backend + +A backend may take a few seconds to accept new connections during peak load. Give connections more time than the default while still failing in a bounded time when the backend is unreachable: + +```toml +[router.upstream.timeouts] +connect_timeout_ms = 6000 +``` + +The gateway gives each upstream connection attempt up to 6 seconds; if the backend is down, requests fail after ~6 seconds (`503`) instead of hanging. + +### One slow endpoint on an otherwise fast API + +The API as a whole should respond within 5 seconds, but a single report endpoint legitimately takes longer: + +```yaml +spec: + resilience: + timeout: 5s + operations: + - method: GET + path: /summary + - method: POST + path: /reports + resilience: + timeout: 60s +``` + +`GET /summary` is bounded at 5 seconds; `POST /reports` is allowed up to 60 seconds; both inherit the gateway-level `route_idle_timeout_ms`. + +### Disabling the route timeout for a long-running API + +```yaml +spec: + resilience: + timeout: 0s # no route (response) timeout for this API +``` + +Disabling the route timeout removes only the per-route **response deadline** — it does not make the request truly unbounded. The request is still subject to every other applicable timeout: the upstream `connect_timeout_ms`, the route `idleTimeout`, and the downstream HCM `stream_idle_timeout` / `idle_timeout`, any of which can still terminate a stalled stream or connection. In other words, behaviour is bounded by all applicable gateway- and API-level timeouts. Use with care. diff --git a/en/docs/api-gateway/1.2.0/setup/artifact-templating.md b/en/docs/api-gateway/1.2.0/setup/artifact-templating.md new file mode 100644 index 000000000..3cb891604 --- /dev/null +++ b/en/docs/api-gateway/1.2.0/setup/artifact-templating.md @@ -0,0 +1,119 @@ +--- +title: "Artifact Templating" +description: "Use Go text template expressions in API Platform Gateway YAML artifacts to inject environment variables and dynamic values at startup." +canonical_url: https://wso2.com/api-platform/docs/api-gateway/setup/artifact-templating/ +md_url: https://wso2.com/api-platform/docs/api-gateway/setup/artifact-templating.md +tags: + - api-gateway + - configuration + - templating +author: WSO2 API Platform Documentation Team +last_updated: 2026-06-17 +content_type: "reference" +--- + +# Gateway Artifact Templating + + +Gateway artifact files (YAML/JSON) support Go `text/template` expressions for injecting dynamic values at startup. Templates are rendered on the raw artifact string before YAML/JSON parsing, so expressions work in any string field across the entire artifact — `upstream`, `auth`, policy `params`, metadata, etc. + +## Available functions + +### `env` + +Reads a value from an environment variable. + + +{% raw %} +``` +{{ env "KEY" }} +``` +{% endraw %} + +Returns an empty string if the variable is not set. Use `| default` to provide a fallback: + +{% raw %} +``` +{{ env "BACKEND_URL" | default "http://localhost:8080" }} +``` +{% endraw %} + +### `required` + +Like `env`, but fails at startup if the variable is not set or empty. Use this to enforce that a value must be provided: + +{% raw %} +``` +{{ required "BACKEND_URL" }} +``` +{% endraw %} + +If the variable is missing, the gateway will not start and will log which variable is required. + +### `redact` + +Marks a resolved value for redaction in config dumps. Any field rendered with `| redact` will appear as `***REDACTED***` in the gateway's config dump output instead of its actual value. + +{% raw %} +``` +{{ env "API_KEY" | redact }} +``` +{% endraw %} + +Use `| redact` for any sensitive value injected from an environment variable (API keys, tokens, passwords). + +### `default` + +Provides a fallback value when the input is empty: + +{% raw %} +``` +{{ env "LOG_LEVEL" | default "info" }} +``` +{% endraw %} + +Can be combined with any other function in a pipeline. + +## Combining functions + +Functions compose as pipelines: + +{% raw %} +``` +{{ env "KEY" | default "fallback" }} +{{ env "KEY" | redact }} +{{ required "KEY" | redact }} +``` +{% endraw %} + +## Example + +{% raw %} +```yaml +apiVersion: gateway.api-platform.wso2.com/v1alpha1 +kind: RestApi +metadata: + name: orders-api-v1 +spec: + context: /orders/v1 + upstream: + main: + url: '{{ env "ORDERS_BACKEND_URL" | default "http://orders-svc:8080" }}' + auth: + apiKey: '{{ required "ORDERS_API_KEY" | redact }}' + policies: + - name: set-headers + version: v1 + params: + request: + headers: + - name: X-Env + value: '{{ env "DEPLOYMENT_ENV" | default "production" }}' +``` +{% endraw %} + +In this example: + +- `ORDERS_BACKEND_URL` is optional — falls back to the default URL if not set. +- `ORDERS_API_KEY` is required — the gateway will not start if it is missing, and the value is redacted in config dumps. +- `DEPLOYMENT_ENV` is optional with a default of `production`. diff --git a/en/docs/api-gateway/1.2.0/setup/configuration.md b/en/docs/api-gateway/1.2.0/setup/configuration.md new file mode 100644 index 000000000..13725def0 --- /dev/null +++ b/en/docs/api-gateway/1.2.0/setup/configuration.md @@ -0,0 +1,162 @@ +--- +title: "Gateway Configuration and Environment Interpolation" +description: "How the API Platform Gateway loads its config.toml, injects environment values through interpolation tokens, and bootstraps required keys and certificates with the setup script." +canonical_url: https://wso2.com/api-platform/docs/api-gateway/setup/configuration/ +md_url: https://wso2.com/api-platform/docs/api-gateway/setup/configuration.md +tags: + - api-gateway + - configuration + - interpolation +author: WSO2 API Platform Documentation Team +last_updated: 2026-07-22 +content_type: "reference" +--- + +# Gateway Configuration and Environment Interpolation + +The Gateway Controller and Policy Engine read their configuration from a TOML file (`config.toml`) layered over built-in defaults. This page explains how configuration is delivered, how environment values are injected through interpolation tokens, and how the one-time setup script provisions the keys and certificates the gateway requires. + +## How configuration is loaded + +Configuration is read from a TOML file mounted at `/etc/gateway-controller/config.toml` (controller) and `/etc/policy-engine/config.toml` (policy engine), layered over the built-in defaults. + +!!! important "Environment variables do not override config keys directly" + There is **no `APIP_GW_*` prefix that auto-maps environment variables onto config keys.** An environment value reaches a setting **only** through an explicit interpolation token written into the config file, resolved when the file is loaded. A field with no token always takes its literal TOML value (or the built-in default). + + This replaces the previous behavior, where an `APIP_GW_`-prefixed environment variable was mapped onto the matching config key and silently overrode the file. A bare `APIP_GW_*` variable with no matching token in `config.toml` is now ignored. + +## Interpolation tokens + +Two functions are available inside `config.toml`: + +{% raw %} + +| Token | Behavior | +|-------|----------| +| `{{ env "NAME" "default" }}` | Substitutes the value of environment variable `NAME`. If the variable is unset **or set-but-empty**, the `default` is used. | +| `{{ file "PATH" }}` | Reads a secret value from a mounted file at `PATH` — for injecting secrets from a mounted volume rather than an environment variable. | + +An example from the shipped `config.toml`: + +```toml +[controller.controlplane] +host = '{{ env "APIP_GW_CONTROLLER_CONTROLPLANE_HOST" "" }}' +token = '{{ env "APIP_GW_CONTROLLER_CONTROLPLANE_TOKEN" "" }}' + +[controller.storage] +type = '{{ env "APIP_GW_CONTROLLER_STORAGE_TYPE" "sqlite" }}' +``` + +{% endraw %} + +Every token in the shipped config carries a default, so an unset variable keeps the built-in value. + +!!! note "`APIP_GW_` is a naming convention, not a prefix override" + The shipped `config.toml` names its token arguments `APIP_GW_` (for example, `APIP_GW_CONTROLLER_STORAGE_TYPE` for `controller.storage.type`). This is purely a convention that makes the token argument readable — it is the literal string passed to the interpolation function, not a prefix that the loader interprets. Renaming the environment variable also requires editing the matching token in the config file. + +### Common tokens + +The shipped `config.toml` already carries interpolation tokens for the settings the sample deployments inject. The most common are: + +| Environment variable (token argument) | Config key | Description | +|----------------------------------------|------------|-------------| +| `APIP_GW_CONTROLLER_STORAGE_TYPE` | `controller.storage.type` | `sqlite`, `postgres`, `sqlserver`, or `memory` | +| `APIP_GW_CONTROLLER_STORAGE_SQLITE_PATH` | `controller.storage.sqlite.path` | Path to the SQLite database file | +| `APIP_GW_CONTROLLER_STORAGE_POSTGRES_PASSWORD` | `controller.storage.postgres.password` | PostgreSQL password | +| `APIP_GW_CONTROLLER_STORAGE_DATABASE_DSN` | `controller.storage.database.dsn` | SQL Server DSN (when storage type is `sqlserver`) | +| `APIP_GW_CONTROLLER_CONTROLPLANE_HOST` | `controller.controlplane.host` | Control plane endpoint (`host:port`) | +| `APIP_GW_CONTROLLER_CONTROLPLANE_TOKEN` | `controller.controlplane.token` | Control plane registration token | +| `APIP_GW_CONTROLLER_LOGGING_LEVEL` | `controller.logging.level` | `debug`, `info`, `warn`, `error` | + +For the complete list of tokens and every configurable option, refer to the [config template](https://github.com/wso2/api-platform/blob/main/gateway/configs/config-template.toml). + +!!! tip "Distinct from artifact templating" + The interpolation tokens described here apply to the gateway's **own `config.toml`**. API **artifacts** (RestApi, LLMProvider, and other resource files) support a separate, richer set of template functions (`env`, `required`, `redact`, `default`) applied to the artifact body. See [Artifact Templating](./artifact-templating.md) for that mechanism. + +## Delivering environment values + +For Docker Compose deployments, the sample composes deliver these values to the container from an `api-platform.env` file via the Compose `env_file:` directive: + +```yaml +services: + gateway-controller: + env_file: + - ./api-platform.env +``` + +`api-platform.env` is generated by the [setup script](#one-time-setup-with-the-setup-script). Add or edit variables there — for example, to connect to a control plane: + +```bash +# api-platform.env +APIP_GW_CONTROLLER_CONTROLPLANE_HOST=your-control-plane-host:9443 +APIP_GW_CONTROLLER_CONTROLPLANE_TOKEN= +``` + +For Kubernetes/Helm deployments, the chart renders `config.toml` into a ConfigMap and injects the runtime secrets (control plane token, database password) as interpolation tokens backed by Kubernetes Secrets — see [Security Hardening](../deployment/production-deployment/security-hardening.md) and [Database Configuration](../deployment/production-deployment/database-configuration.md). + +## No development / demo mode + +The gateway has **no development or demo mode** and **never auto-generates keys or certificates**. It fails closed with a descriptive error at startup if a required key or certificate is missing. Everything the gateway requires must be provisioned before it starts: + +- **AES-256 at-rest encryption key** — used to encrypt sensitive data at rest. Required; the controller will not start without it. +- **Router HTTPS listener certificate** — the TLS certificate/key for the router's HTTPS listener. +- **Admin credentials** — the gateway-controller management API basic-auth credential. +The controller fails closed if basic auth is enabled with no credential. +- **`api-platform.env`** — runtime settings read directly by the gateway-runtime entrypoint (for example, `GATEWAY_CONTROLLER_HOST`, `LOG_LEVEL`). + +The setup script provisions all four. + +## One-time setup with the setup script + +The distribution ships `scripts/setup.sh` (and `scripts/setup.ps1`, its Windows PowerShell counterpart), which provisions everything a fresh gateway needs. Run it once before the first `docker compose up`: + +=== "Linux / macOS" + + ```bash + ./scripts/setup.sh + docker compose up + ``` + +=== "Windows (PowerShell)" + + ```powershell + powershell -ExecutionPolicy Bypass -File .\scripts\setup.ps1 + docker compose up + ``` + +The setup script provisions, idempotently: + +| Artifact | Location | Purpose | +|----------|----------|---------| +| Router listener certificate | `listener-certs/default-listener.{crt,key}` | Self-signed cert for the router HTTPS listener (SANs include `localhost`, `*.localhost`, `host.docker.internal`, `127.0.0.1`). | +| AES-256 encryption key | `aesgcm-keys/default-aesgcm256-v1.bin` | 32-byte at-rest encryption key, bind-mounted into the controller. | +| Admin credentials | `api-platform.env` | Gateway-controller REST/management API basic-auth credential (`APIP_GW_CONTROLLER_AUTH_BASIC_ADMIN_USERNAME` + bcrypt `..._PASSWORD_HASH`) | +| `api-platform.env` | `api-platform.env` | Runtime defaults loaded into the containers via `env_file`. | + +!!! note "Admin credentials" + The gateway-controller management API is protected by basic auth. You provide the plaintext + `ADMIN_USERNAME` (defaults to `admin`) and `ADMIN_PASSWORD` (used if set, otherwise prompted, + otherwise randomly generated) to the setup script; it writes + `APIP_GW_CONTROLLER_AUTH_BASIC_ADMIN_USERNAME` and the **bcrypt** `..._PASSWORD_HASH` into + `api-platform.env` (the tokens `config.toml` reads) and prints the plaintext password **once** — copy + it. For non-interactive use: `ADMIN_USERNAME=admin ADMIN_PASSWORD='…' ./scripts/setup.sh` (on Windows: + `$env:ADMIN_USERNAME='admin'; $env:ADMIN_PASSWORD='…'; powershell -ExecutionPolicy Bypass -File .\scripts\setup.ps1`). If those + tokens are unset when the controller starts with the shipped `config.toml`, it **refuses to start** + rather than running on an empty credential. + +The script is **idempotent** — existing files are kept, not overwritten. Flags: + +| Flag | Effect | +|------|--------| +| `--force` | Regenerate the certificate and encryption key (rotating them), rewrite `api-platform.env`, and re-provision the admin credentials (rotating the password). | +| `--certs-only` | Generate only the listener TLS certificate; skip the encryption key, admin credentials, and `api-platform.env`. | + +!!! warning "Rotating the encryption key" + Running the setup script with `--force` regenerates the AES-256 encryption key. Data encrypted with the previous key becomes unreadable. Only rotate the key deliberately. + +!!! note "Control plane connection is not configured by the script" + Connecting to a control plane is optional and is not configured by the setup script. To connect, add `APIP_GW_CONTROLLER_CONTROLPLANE_HOST` and `APIP_GW_CONTROLLER_CONTROLPLANE_TOKEN` to `api-platform.env` (both default to empty, which runs the gateway in standalone mode). + +--- + +[← Artifact Templating](./artifact-templating.md)  |  [Setting Up the Database →](./database-setup.md) diff --git a/en/docs/api-gateway/1.2.0/setup/database-setup.md b/en/docs/api-gateway/1.2.0/setup/database-setup.md new file mode 100644 index 000000000..ba123f1d0 --- /dev/null +++ b/en/docs/api-gateway/1.2.0/setup/database-setup.md @@ -0,0 +1,337 @@ +--- +title: "Setting Up the Database" +description: "Create the database and apply the Gateway Controller schema for PostgreSQL or SQL Server before starting the API Platform Gateway." +canonical_url: https://wso2.com/api-platform/docs/api-gateway/setup/database-setup/ +md_url: https://wso2.com/api-platform/docs/api-gateway/setup/database-setup.md +tags: + - api-gateway + - configuration + - postgresql + - sqlserver + - devops +author: WSO2 API Platform Documentation Team +last_updated: 2026-07-30 +content_type: "how-to" +--- + +# Setting Up the Database + +The Gateway Controller persists API configurations, subscriptions, applications, keys, and other metadata in a database. Three storage backends are supported, selected through `[controller.storage].type`: + +| `type` | Description | Schema provisioning | +|--------|-------------|---------------------| +| `sqlite` (default) | Embedded, file-based database (`./data/gateway.db`). Single replica only. | Created and migrated automatically on startup | +| `postgres` | External PostgreSQL. Required for multi-replica, high-availability deployments. | Must be provisioned before the controller starts | +| `sqlserver` | External Microsoft SQL Server. Required for multi-replica, high-availability deployments. | Must be provisioned before the controller starts | + +If you are using `sqlite`, there is nothing to do — the controller creates the database file itself on first start, and the rest of this page does not apply. +For **PostgreSQL** and **SQL Server**, the Gateway Controller connects to the database you point it at but does not run schema DDL against it. + +## Before You Begin + +- A running PostgreSQL or SQL Server instance that is reachable from every Gateway Controller replica. +- An administrative account on that instance that can create databases, logins, and tables. +- A database client on the machine you run the provisioning from — `psql` for PostgreSQL, `sqlcmd` for SQL Server. + +## Get the Schema Scripts + +The scripts ship inside the gateway distribution: + +```text +wso2apip-api-gateway-/ +└── resources/ + └── gateway-controller/ + └── db-scripts/ + ├── gateway-controller-db.postgres.sql + └── gateway-controller-db.sqlserver.sql +``` + +If you are deploying from container images or Helm rather than the distribution zip, download them from the repository instead: + +- [gateway-controller-db.postgres.sql](https://github.com/wso2/api-platform/blob/main/gateway/gateway-controller/pkg/storage/gateway-controller-db.postgres.sql) +- [gateway-controller-db.sqlserver.sql](https://github.com/wso2/api-platform/blob/main/gateway/gateway-controller/pkg/storage/gateway-controller-db.sqlserver.sql) + +!!! important + Always use the scripts that ship with the gateway version you are deploying. Applying a script from a different release can leave the schema out of step with what the controller expects. + +## Step 1 - Create the Database and User + +Create an empty database and a dedicated account for the gateway. +(The schema should be applied by an account with DDL privileges.) + +=== "PostgreSQL" + + Connect as an administrator: + + ```bash + psql "host= port=5432 dbname=postgres user= sslmode=require" + ``` + + Create the database and a login for the gateway. + + ```sql + CREATE DATABASE gateway_controller; + CREATE USER gateway WITH PASSWORD 'your-db-password'; + ``` + +=== "SQL Server" + + Connect as an administrator: + + ```bash + sqlcmd -S ,1433 -U -P '' + ``` + + Create the database, login, and user. + + ```sql + CREATE DATABASE gateway_controller; + GO + CREATE LOGIN gateway WITH PASSWORD = 'your-db-password'; + GO + USE gateway_controller; + GO + CREATE USER gateway FOR LOGIN gateway; + GO + ``` + +## Step 2 - Apply the Schema + +Run the script for your database against the database you just created. + +=== "PostgreSQL" + + ```bash + psql "host= port=5432 dbname=gateway_controller user= sslmode=require" \ + -v ON_ERROR_STOP=1 \ + -f resources/gateway-controller/db-scripts/gateway-controller-db.postgres.sql + ``` + + `ON_ERROR_STOP=1` makes `psql` abort and return a non-zero exit code on the first failing statement, instead of continuing and leaving a partially created schema. + +=== "SQL Server" + + ```bash + sqlcmd -S ,1433 -d gateway_controller \ + -U -P '' -b \ + -i resources/gateway-controller/db-scripts/gateway-controller-db.sqlserver.sql + ``` + + `-b` makes `sqlcmd` exit with an error code if any statement in the batch fails. + + !!! tip + `sqlcmd` v18 and later negotiate an encrypted connection by default and reject certificates they cannot validate. For production, use `-N` with a properly trusted certificate so the connection is both encrypted and verified. Only add `-C` if you must connect to a server with a self-signed certificate — it disables certificate validation entirely, so treat it as a controlled, limited-use exception rather than a default troubleshooting flag. + +The scripts are idempotent — every object is guarded (`CREATE TABLE IF NOT EXISTS` on PostgreSQL, `IF OBJECT_ID(...) IS NULL` on SQL Server) — so re-running them is safe and creates only what is missing. + +## Step 3 - Apply the Event Gateway Schema (Event Gateway Only) + +The Event Gateway stores WebSub and WebBroker artifacts in the same database, in three tables the core script does not define: `websub_apis`, `webbroker_apis`, and `webhook_secrets`. Like the core schema, these are auto-created only for `sqlite`; for external databases you must apply them yourself. + +Apply the matching supplemental script after Step 2: + +- [eventgateway-db.postgres.sql](https://github.com/wso2/api-platform/blob/main/event-gateway/gateway-controller/pkg/dbschema/eventgateway-db.postgres.sql) +- [eventgateway-db.sqlserver.sql](https://github.com/wso2/api-platform/blob/main/event-gateway/gateway-controller/pkg/dbschema/eventgateway-db.sqlserver.sql) + +=== "PostgreSQL" + + ```bash + psql "host= port=5432 dbname=gateway_controller user= sslmode=require" \ + -v ON_ERROR_STOP=1 -f eventgateway-db.postgres.sql + ``` + +=== "SQL Server" + + ```bash + sqlcmd -S ,1433 -d gateway_controller \ + -U -P '' -b \ + -i eventgateway-db.sqlserver.sql + ``` + +Skip this step if you are not running the Event Gateway. + +## Step 4 - Grant Gateway Access + +With the tables in place give `gateway` the privileges the controller needs at runtime: `SELECT`, `INSERT`, `UPDATE`, and `DELETE`. + +=== "PostgreSQL" + + ```bash + psql "host= port=5432 dbname=gateway_controller user= sslmode=require" + ``` + + ```sql + GRANT CONNECT ON DATABASE gateway_controller TO gateway; + GRANT USAGE ON SCHEMA public TO gateway; + GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO gateway; + ``` + +=== "SQL Server" + + ```bash + sqlcmd -S ,1433 -d gateway_controller -U -P '' + ``` + + ```sql + ALTER ROLE db_datareader ADD MEMBER gateway; + ALTER ROLE db_datawriter ADD MEMBER gateway; + GO + ``` + +## Step 5 - Verify the Schema + +Confirm the tables exist before starting the gateway. + +=== "PostgreSQL" + + ```bash + psql "host= port=5432 dbname=gateway_controller user= sslmode=require" \ + -c "SELECT table_name FROM information_schema.tables WHERE table_schema = 'public' ORDER BY table_name;" + ``` + +=== "SQL Server" + + ```bash + sqlcmd -S ,1433 -d gateway_controller \ + -U -P '' \ + -Q "SELECT name FROM sys.tables ORDER BY name;" + ``` + +The core schema creates 15 tables: + +```text +api_keys application_api_keys applications +artifacts certificates events +gateway_states llm_providers llm_provider_templates +llm_proxies mcp_proxies rest_apis +secrets subscription_plans subscriptions +``` + +If you also applied the Event Gateway script, `websub_apis`, `webbroker_apis`, and `webhook_secrets` are present as well. + +## Step 6 - Point the Gateway Controller at the Database + +With the schema in place, configure the connection in `configs/config.toml`. + +=== "PostgreSQL" + + ```toml + [controller.storage] + type = "postgres" + + [controller.storage.postgres] + host = "" + port = 5432 + database = "gateway_controller" + user = "gateway" + password = '{% raw %}{{ env "APIP_GW_CONTROLLER_STORAGE_POSTGRES_PASSWORD" "" }}{% endraw %}' + sslmode = "require" # disable, require, verify-ca, verify-full + ``` + +=== "SQL Server" + + SQL Server uses the unified `[controller.storage.database]` block. TLS behavior is controlled by `options` rather than PostgreSQL's `sslmode`. Two forms are supported — if `dsn` is set, the discrete fields below it are ignored. + + The shipped Compose files use a single `dsn`, so no password is ever written into the file: + + ```toml + [controller.storage] + type = "sqlserver" + + [controller.storage.database] + driver = "sqlserver" + dsn = '{% raw %}{{ env "APIP_GW_CONTROLLER_STORAGE_DATABASE_DSN" "" }}{% endraw %}' + + [controller.storage.database.options] + encrypt = "true" # disable, false, true, strict + trust_server_certificate = "false" + ``` + + Discrete fields, the same shape as PostgreSQL's, are also supported — useful outside the reference Compose setup, e.g. under Kubernetes/Helm: + + ```toml + [controller.storage] + type = "sqlserver" + + [controller.storage.database] + driver = "sqlserver" + host = "" + port = 1433 + database = "gateway_controller" + user = "gateway" + password = '{% raw %}{{ env "APIP_GW_CONTROLLER_STORAGE_DATABASE_PASSWORD" "" }}{% endraw %}' + + [controller.storage.database.options] + encrypt = "true" # disable, false, true, strict + trust_server_certificate = "false" + ``` + + Unlike `dsn` and PostgreSQL's fields, these discrete SQL Server fields don't ship with an interpolation token by default — add one following the pattern below rather than writing the password literally. + +The {% raw %}`{{ env "..." "" }}`{% endraw %} form is the interpolation token already used in the shipped `config.toml` — it reads the value from an environment variable at load time instead of storing it in the file. Set the actual values in `api-platform.env`: + +```bash +# api-platform.env +APIP_GW_CONTROLLER_STORAGE_TYPE=postgres +APIP_GW_CONTROLLER_STORAGE_POSTGRES_HOST= +APIP_GW_CONTROLLER_STORAGE_POSTGRES_DATABASE=gateway_controller +APIP_GW_CONTROLLER_STORAGE_POSTGRES_USER=gateway +APIP_GW_CONTROLLER_STORAGE_POSTGRES_PASSWORD=your-db-password +``` + +For SQL Server, the shipped Compose files supply the whole connection string through `APIP_GW_CONTROLLER_STORAGE_DATABASE_DSN`. See [Gateway Configuration and Environment Interpolation](./configuration.md) for how interpolation works. For the full list of storage configuration options for both databases, refer to the [config template](https://github.com/wso2/api-platform/blob/main/gateway/configs/config-template.toml). + +Start the gateway: + +```bash +docker compose up +``` + +On startup the controller logs that it connected to the external database and that schema auto-apply was skipped. That message is expected — it confirms the controller is relying on the schema you provisioned. + +## Kubernetes Deployments + +The Helm charts do not include a bootstrap job that provisions the schema, so the same steps apply: create the database and run the scripts before `helm install`. Run them from any host with network access to the database — a CI job, a bastion host, or a temporary pod in the cluster: + +```bash +kubectl run psql-client --rm -it --restart=Never \ + --namespace \ + --image=postgres:16 -- \ + psql "host= port=5432 dbname=gateway_controller user= sslmode=require" +``` + +Once the schema exists, follow [Database Configuration](../deployment/production-deployment/database-configuration.md) to wire the chart to the database and inject the password from a Kubernetes secret. + +## Redis for Distributed Rate Limiting (Optional) + +To enable distributed rate limiting across multiple Gateway Runtime instances, configure the rate limiting policy to use Redis as the backend: + +```toml +[policy_configurations.ratelimit_v1] +algorithm = "fixed-window" +backend = "redis" + +[policy_configurations.ratelimit_v1.redis] +host = "redis.example.com" +port = 6379 +password = '{% raw %}{{ env "APIP_GW_RATELIMIT_REDIS_PASSWORD" "" }}{% endraw %}' +``` + +`config.toml` is interpolated the same way everywhere in the file, so the {% raw %}`{{ env "..." "" }}`{% endraw %} token above works here too — see [Gateway Configuration and Environment Interpolation](./configuration.md) rather than writing the password literally. + +For the full list of Redis configuration options, refer to the [Advanced Rate Limiting documentation](https://wso2.com/api-platform/policy-hub/policies/advanced-ratelimit). + +## Troubleshooting + +| Symptom | Cause | Fix | +|---------|-------|-----| +| PostgreSQL: `ERROR: relation "artifacts" does not exist` | The schema was never applied, or was applied to a different database | Re-run Step 2 against the database named in `[controller.storage.postgres].database` | +| SQL Server: `Invalid object name 'dbo.artifacts'` | Same as above | Re-run Step 2 against the database named in `[controller.storage.database].database` | +| SQL Server: `Msg 1934 ... CREATE INDEX failed ... 'QUOTED_IDENTIFIER'` | The script is from a release before the `SET` options were added | Use the script shipped with your gateway version | +| `permission denied for table ...` at runtime | Step 4 was skipped, or was run before Step 2/3 finished creating the tables it grants access to | Run [Step 4 - Grant Gateway Access](#step-4-grant-gateway-access) | +| Event Gateway fails on `websub_apis` / `webbroker_apis` / `webhook_secrets` | The supplemental Event Gateway script was not applied | Run Step 3, then re-run Step 4 so `gateway` gets access to the new tables | +| Controller connects but logs that schema auto-apply was skipped | Expected behavior for external databases | No action needed | + +--- + +[← Configuration & Interpolation](./configuration.md)  |  [Artifact Templating →](./artifact-templating.md)