From 856b999ced9d5dd60d283b0ddc8e16f3eaf895b2 Mon Sep 17 00:00:00 2001 From: Renuka Fernando Date: Sun, 2 Aug 2026 14:00:59 +0530 Subject: [PATCH 1/4] chore(gateway): add local-debug config overlay for VS Code debugging Introduce configs/config-debug.toml, a small overlay layered on the shipped config.toml via a second repeatable -config flag (merged last-wins), so host-process debug settings stay out of the distributed config.toml: - policy-engine ext_proc + ALS + python-executor TCP modes and host.docker.internal, plus the dev AES-GCM key path - wire the overlay into the Gateway Controller and Policy Engine (xDS / File) launch configs; drop the dead double-underscore env vars - DEBUG_GUIDE.md: run the builder before the controller, document the overlay + one-time AES key provisioning, and remove the config.toml edit/revert dance Signed-off-by: Renuka Fernando --- .vscode/launch.json | 75 ++++++++------- gateway/DEBUG_GUIDE.md | 96 ++++++++++++------- gateway/configs/config-debug.toml | 66 +++++++++++++ gateway/examples/petstore-api.yaml | 11 +-- .../gateway-controller/pkg/version/version.go | 2 +- 5 files changed, 171 insertions(+), 79 deletions(-) create mode 100644 gateway/configs/config-debug.toml diff --git a/.vscode/launch.json b/.vscode/launch.json index 3cd534f1ea..8ad2b9fc68 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -54,43 +54,36 @@ "request": "launch", "mode": "auto", "program": "${workspaceFolder}/gateway/gateway-controller/cmd/controller", + "cwd": "${workspaceFolder}/gateway/gateway-controller", "args": [ - "-config", - "${workspaceFolder}/gateway/configs/config.toml", + // Shipped base config first, then the local debug overlay. -config is + // repeatable, merged last-wins per key, so config-debug.toml flips the + // policy-engine/ALS connection modes to TCP without editing config.toml. + "-config", "${workspaceFolder}/gateway/configs/config.toml", + "-config", "${workspaceFolder}/gateway/configs/config-debug.toml", ], "env": { - // Controlplane connectivity details - "APIP_GW_CONTROLPLANE_HOST": "localhost:9243", // localhost:9243 for locally running Platform API - "APIP_GW_GATEWAY_REGISTRATION_TOKEN": "", - // Database configuration for the controller - "APIP_GW_CONTROLLER_STORAGE_TYPE": "sqlite", // sqlite, postgres - //// SQLite (default) + // Dev admin login - admin:admin + "APIP_GW_CONTROLLER_AUTH_BASIC_ADMIN_USERNAME": "admin", + "APIP_GW_CONTROLLER_AUTH_BASIC_ADMIN_PASSWORD_HASH": "$2y$10$14lPL4.LZJ0U8vwVGGBzpucXs2XqcDnnmQaTRgNRylCRlj81XuyGO", + // Control plane connectivity — empty = standalone (no registration) + "APIP_GW_CONTROLLER_CONTROLPLANE_HOST": "localhost:9243", + "APIP_GW_CONTROLLER_CONTROLPLANE_TOKEN": "", + // Storage (sqlite). config.toml carries the {{ env }} tokens for these. + "APIP_GW_CONTROLLER_STORAGE_TYPE": "sqlite", "APIP_GW_CONTROLLER_STORAGE_SQLITE_PATH": "${workspaceFolder}/gateway/gateway-controller/data/gateway.db", - //// PostgreSQL - "APIP_GW_CONTROLLER_STORAGE_POSTGRES_HOST": "localhost", - "APIP_GW_CONTROLLER_STORAGE_POSTGRES_PORT": "5432", - "APIP_GW_CONTROLLER_STORAGE_POSTGRES_DATABASE": "gateway", - "APIP_GW_CONTROLLER_STORAGE_POSTGRES_USER": "gateway", - "APIP_GW_CONTROLLER_STORAGE_POSTGRES_PASSWORD": "gateway", - "APIP_GW_CONTROLLER_STORAGE_POSTGRES_SSLMODE": "disable", - // Paths for file-based configurations - "APIP_GW_CONTROLLER_LLM_TEMPLATE__DEFINITIONS__PATH": "${workspaceFolder}/gateway/gateway-controller/default-llm-provider-templates", - "APIP_GW_CONTROLLER_ROUTER_DOWNSTREAM__TLS_CERT__PATH": "${workspaceFolder}/gateway/gateway-controller/listener-certs/default-listener.crt", - "APIP_GW_CONTROLLER_ROUTER_DOWNSTREAM__TLS_KEY__PATH": "${workspaceFolder}/gateway/gateway-controller/listener-certs/default-listener.key", - "APIP_GW_ROUTER_DOWNSTREAM__TLS_CERT__PATH": "${workspaceFolder}/gateway/gateway-controller/listener-certs/default-listener.crt", - "APIP_GW_ROUTER_DOWNSTREAM__TLS_KEY__PATH": "${workspaceFolder}/gateway/gateway-controller/listener-certs/default-listener.key", - "APIP_GW_ROUTER_LUA_REQUEST__TRANSFORMATION_SCRIPT__PATH": "${workspaceFolder}/gateway/gateway-controller/lua/request_transformation.lua", - // Policy definitions path - "APIP_GW_CONTROLLER_POLICIES_DEFINITIONS__PATH": "${workspaceFolder}/gateway/gateway-builder/target/output/gateway-controller/policies", - // Policy Engine connectivity details - "APIP_GW_ROUTER_POLICY__ENGINE_MODE": "tcp", - "APIP_GW_ROUTER_POLICY__ENGINE_HOST": "host.docker.internal", - "APIP_GW_ANALYTICS_GRPC__EVENT__SERVER_MODE": "tcp", - // Enable development mode - "APIP_GW_DEVELOPMENT_MODE": "true", - // Immutable gateway mode - "APIP_GW_IMMUTABLE__GATEWAY_ENABLED": "false", - "APIP_GW_IMMUTABLE__GATEWAY_ARTIFACTS__DIR": "${workspaceFolder}/gateway/examples", + // Policy definitions path (built by gateway-builder; run it first) + "APIP_GW_CONTROLLER_POLICIES_DEFINITIONS_PATH": "${workspaceFolder}/gateway/gateway-builder/target/output/gateway-controller/policies", + // NOTE: only vars whose {{ env }} token exists in the shipped config.toml + // work here. Debug-only settings NOT in config.toml — policy-engine + // ext_proc/ALS TCP modes, the AES-GCM key, python-executor — live in + // configs/config-debug.toml (loaded via the second -config above), so + // config.toml ships untouched. To debug against Postgres, add a + // [controller.storage] + [controller.storage.postgres] block there. + // Relative paths resolve from cwd (gateway/gateway-controller/): + // listener-certs/default-listener.{crt,key} → router.downstream_tls + // lua/request_transformation.lua → router.lua + // default-llm-provider-templates/ → controller.llm }, }, { @@ -100,12 +93,16 @@ "mode": "auto", "program": "${workspaceFolder}/gateway/gateway-runtime/policy-engine/cmd/policy-engine", "args": [ + // Base config + local debug overlay (config-debug.toml sets ext_proc/ALS + // to TCP and points the python-executor bridge at localhost:9010). "-config", "${workspaceFolder}/gateway/configs/config.toml", + "-config", "${workspaceFolder}/gateway/configs/config-debug.toml", "-xds-server", "localhost:18001", ], "env": { - "APIP_GW_POLICY__ENGINE_SERVER_MODE": "tcp", - "APIP_GW_ANALYTICS_ACCESS__LOGS__SERVICE_MODE": "tcp", + // config-debug.toml (loaded above) supplies the TCP modes. To debug the + // llm-cost policy, add [policy_configurations.llm_cost_v1] pricing_file + // there — config.toml (shipped) intentionally carries no such block. }, }, { @@ -115,12 +112,16 @@ "mode": "auto", "program": "${workspaceFolder}/gateway/gateway-runtime/policy-engine/cmd/policy-engine", "args": [ + // Base config + local debug overlay (config-debug.toml sets ext_proc/ALS + // to TCP and points the python-executor bridge at localhost:9010). "-config", "${workspaceFolder}/gateway/configs/config.toml", + "-config", "${workspaceFolder}/gateway/configs/config-debug.toml", "-policy-chains-file", "${workspaceFolder}/gateway/gateway-runtime/policy-engine/configs/policy-chains.yaml", ], "env": { - "APIP_GW_POLICY__ENGINE_SERVER_MODE": "tcp", - "APIP_GW_ANALYTICS_ACCESS__LOGS__SERVICE_MODE": "tcp", + // config-debug.toml (loaded above) supplies the TCP modes. To debug the + // llm-cost policy, add [policy_configurations.llm_cost_v1] pricing_file + // there — config.toml (shipped) intentionally carries no such block. }, }, { diff --git a/gateway/DEBUG_GUIDE.md b/gateway/DEBUG_GUIDE.md index 7637ba7e18..ebfadda67c 100644 --- a/gateway/DEBUG_GUIDE.md +++ b/gateway/DEBUG_GUIDE.md @@ -116,6 +116,43 @@ curl http://localhost:8080/petstore/v1/pets --- +## Local debug config overlay + +Both local-process options (2A and 2B) need a handful of settings that differ from a production container — chiefly the policy-engine ext_proc, ALS, and Python-executor connections switching from **UDS to TCP**, because there is no shared Unix socket between the Docker router and the host-run processes. + +These values live in **`gateway/configs/config-debug.toml`**, a small overlay that is layered on top of the shipped `config.toml`: + +- `-config` is **repeatable** on both the gateway-controller and policy-engine binaries. The loaders merge the files in the order given, **last-wins per key** (a key set in a later file overrides the same key from an earlier one), deep-merging sections and leaving every unlisted key untouched. +- The **Gateway Controller**, **Policy Engine - xDS**, and **Policy Engine - File** launch configs already pass both files: + + ```jsonc + "args": [ + "-config", "${workspaceFolder}/gateway/configs/config.toml", + "-config", "${workspaceFolder}/gateway/configs/config-debug.toml", + ] + ``` + +Why an overlay instead of editing `config.toml` or setting env vars: + +- **`config.toml` ships in the distribution** — it must stay production-clean. The overlay keeps debug-only values out of it, and is **never mounted into the container** (the container reads `config.toml` directly, in UDS mode), so there is nothing to remember to revert. +- **Env vars only work where `config.toml` references them** via a `{{ env "NAME" }}` token — there is no prefix-based override. The overlay sets concrete values directly, with no dependency on token names matching. + +Workspace-relative **file paths** (policy definitions, DB) stay as `launch.json` env vars — TOML can't expand VS Code's `${workspaceFolder}` — and the Controller's `cwd` is set to `gateway/gateway-controller` so `config.toml`'s relative defaults (certs, lua, LLM templates) resolve to the checked-in dirs there. The **one file the overlay does carry** is the AES-GCM encryption key (`[[controller.encryption.providers]]`): the shipped `config.toml` has no encryption section, so without it the host-run controller falls back to a code-default key path that doesn't exist under the debug `cwd` and fails to start. Its value is a stable, repo-relative path (not per-developer), so it lives in the overlay rather than an env var. + +### One-time key provisioning + +The AES-256 at-rest encryption key the overlay points at is **gitignored and not committed** (it's a real secret — see `gateway/.gitignore`). The listener TLS cert/key are already committed, so this key is the only file you need to generate. Create it once from the `gateway/` directory: + +```bash +cd gateway +mkdir -p gateway-controller/aesgcm-keys +( umask 177; openssl rand 32 > gateway-controller/aesgcm-keys/default-aesgcm256-v1.bin ) +``` + +This writes the 32-byte key to exactly the path the debug controller's `cwd` reads. Without it the Gateway Controller **exits at startup** while loading encryption providers. + +--- + ## Option 2A: Local Process Debug — Controller + Policy Engine in VS Code Gateway Controller and Policy Engine run as local VS Code processes. Only the Envoy Router runs in Docker Compose. @@ -147,8 +184,15 @@ graph TB - VS Code with Go extension installed - Docker and Docker Compose - Control plane host and registration token (optional, for gateway registration) +- **One-time key provisioning** — generate the gitignored AES-256 encryption key the controller loads (see [One-time key provisioning](#one-time-key-provisioning)). The controller **won't start** without it. + +### Step 1: Run Gateway Builder + +Run the **Gateway Builder** debug configuration from VS Code. This compiles all policies and generates the policy-engine binary into `gateway/gateway-builder/target/output/`. It also writes the policy **definition** files into `gateway/gateway-builder/target/output/gateway-controller/policies/` — the directory the controller reads via `APIP_GW_CONTROLLER_POLICIES_DEFINITIONS_PATH`. + +> **Note:** This is the slowest step, so start it first — it can compile in the background while you do Steps 2–3. It **must** finish before you start the Gateway Controller (Step 4): the controller loads these policy definitions once at startup (before it hydrates stored configs and builds the first xDS snapshot). If the directory is still empty because the builder hasn't finished, the controller starts with **zero** policy definitions — with no hard error — and you must restart it after the builder completes. -### Step 1: Configure Control Plane Connection +### Step 2: Configure Control Plane Connection Update `.vscode/launch.json` in the **Gateway Controller** configuration with your control plane details: @@ -169,7 +213,7 @@ Update `.vscode/launch.json` in the **Gateway Controller** configuration with yo > and the matching token for the registration token). Leave them empty (`""`) to run in standalone > mode without a control plane connection. -### Step 2: Update Docker Compose Configuration +### Step 3: Update Docker Compose Configuration In `gateway/docker-compose.yaml`, make two changes to the `gateway-runtime` service: @@ -200,16 +244,10 @@ services: # - "9003:9003" # Metrics ``` -### Step 3: Start Gateway Controller +### Step 4: Start Gateway Controller Run the **Gateway Controller** debug configuration from VS Code. -### Step 4: Run Gateway Builder - -Run the **Gateway Builder** debug configuration from VS Code. This compiles all policies and generates the policy-engine binary into `gateway/gateway-builder/target/output/`. - -> **Note:** Wait for the builder to complete successfully before starting the Policy Engine. - ### Step 5: Start Policy Engine Run the **Policy Engine - xDS** debug configuration from VS Code. @@ -226,18 +264,19 @@ docker compose logs -ft gateway-runtime sample-backend ### Step 7: Deploy an API and Test -Deploy a test API via the Gateway Controller REST API: +Deploy a test API via the Gateway Controller REST API. The `-u "admin:admin"` below is the **local-only** debug login set by the `Gateway Controller` launch config (`config-debug.toml`); it is never a shipped or deployable credential. Against a real control plane, pass your own instead, e.g. `-u "$APIP_ADMIN_USER:$APIP_ADMIN_PASS"`. ```bash -curl -X POST http://localhost:9090/api/management/v0.9/rest-apis \ +curl -X POST http://localhost:9090/api/management/v1/rest-apis \ -H "Content-Type: application/yaml" \ - --data-binary @path/to/api.yaml + -u "admin:admin" \ + --data-binary @examples/petstore-api.yaml ``` Send a request to the deployed API: ```bash -curl http://localhost:8080/petstore/v1/pets +curl http://localhost:8080/petstoretest/pet/85964529 ``` --- @@ -278,22 +317,23 @@ graph TB - VS Code with Go and Python extensions installed - Docker and Docker Compose - Control plane host and registration token (optional, for gateway registration) +- **One-time key provisioning** — generate the gitignored AES-256 encryption key the controller loads (see [One-time key provisioning](#one-time-key-provisioning)). The controller **won't start** without it. -### Step 1: Enable TCP Mode in config.toml +### Step 1: TCP Mode (no action needed) -Add the following block to `configs/config.toml`: +The Policy Engine reaches the host-run Python Executor over TCP at `localhost:9010`. This is **already configured** in the local debug overlay `configs/config-debug.toml`, which the **Policy Engine - xDS** launch config loads as a second `-config` on top of `config.toml`: ```toml +# configs/config-debug.toml (already committed) [policy_engine.python_executor.server] mode = "tcp" port = 9010 host = "localhost" ``` -This tells the Policy Engine to connect to the Python Executor over TCP instead of the default Unix domain socket. +No `config.toml` edit is required, and there is nothing to revert afterward. Because the overlay is passed only on the launch config's command line — never mounted into the container — the shipped `config.toml` stays in UDS mode, so the containerized Policy Engine is unaffected. -> [!WARNING] -> **Remove this block when you are done debugging.** The `config.toml` is also mounted into the Docker container (`docker-compose.yaml`), where the Python Executor runs in UDS mode. If this TCP block is left in, the containerized Policy Engine will try to dial `localhost:9010` while the embedded Python Executor is listening on a UDS socket — causing silent connection failures. +> **How it works:** `-config` is repeatable; the loaders merge the files in order with last-wins precedence, so the overlay's `tcp` values override the `uds` defaults from `config.toml` for the host-run process only. See [Local debug config overlay](#local-debug-config-overlay). ### Step 2: Run Gateway Builder @@ -423,20 +463,9 @@ curl -X POST http://localhost:8080/your-api/chat \ ### Step 10: Clean Up -When you are done debugging: - -1. **Remove the TCP block** from `configs/config.toml`: +When you are done debugging, **revert the Docker Compose changes** from Step 4 (restore `GATEWAY_CONTROLLER_HOST` and uncomment the Policy Engine ports) so `docker compose up` runs the full containerized stack again. -```diff --[policy_engine.python_executor.server] --mode = "tcp" --port = 9010 --host = "localhost" -``` - -2. **Revert the Docker Compose changes** from Step 4 (restore `GATEWAY_CONTROLLER_HOST` and uncomment Policy Engine ports). - -This ensures `docker compose up` continues to work correctly with UDS mode. +There is nothing to undo in `config.toml` — all TCP/debug settings live in `configs/config-debug.toml`, which is only ever passed to the host-run processes via `launch.json`, never mounted into the container. --- @@ -516,10 +545,7 @@ cp gateway-builder/target/output/python-executor/python_policy_registry.py \ → The Policy Engine is trying to connect to the Python Executor but failing. Check: 1. Is the Python Executor actually running? (`ps aux | grep main.py`) 2. Is it listening on the right address? (should show `localhost:9010`) -3. Does your `config.toml` have the `[policy_engine.python_executor.server]` block with `mode = "tcp"`? +3. Is the **Policy Engine - xDS** launch config loading `configs/config-debug.toml` as a second `-config` (it carries `[policy_engine.python_executor.server] mode = "tcp"`)? **"bind: address already in use" on port 9010** → Kill stale Python Executor processes: `pkill -f "python.*main.py"` - -**Container mode broken after debugging** -→ You likely left `[policy_engine.python_executor.server] mode = "tcp"` in `configs/config.toml`. Remove it — see [Step 10](#step-10-clean-up). diff --git a/gateway/configs/config-debug.toml b/gateway/configs/config-debug.toml new file mode 100644 index 0000000000..06d58883d4 --- /dev/null +++ b/gateway/configs/config-debug.toml @@ -0,0 +1,66 @@ +# ── Local host-process debug overlay ────────────────────────────────────────── +# Layered ON TOP of the shipped configs/config.toml via a second `-config` flag in +# .vscode/launch.json — the loaders merge -config files in order with last-wins +# precedence per key, so these values override the config.toml defaults while +# leaving every other key untouched. +# +# Purpose: keep debug-only settings OUT of config.toml, which ships in the +# distribution zip. This file is loaded ONLY by the host-run Gateway Controller and +# Policy Engine while debugging — it is NEVER mounted into the gateway-runtime +# container (that container reads config.toml directly, in UDS mode). +# +# Scope: connection modes/hosts, plus the one debug-only file the host-run +# controller can't otherwise locate (the AES-GCM key). Component-agnostic — the +# Controller reads [router.policy_engine] + [collector.server] + [controller.encryption]; +# the Policy Engine reads [policy_engine.server] + [collector.server] + +# [policy_engine.python_executor]; each ignores the sections it doesn't use. +# Workspace-relative paths that need VS Code's ${workspaceFolder} stay in launch.json +# env vars; the encryption key path below is relative to the controller's debug +# `cwd` (gateway/gateway-controller), set in launch.json. + +# Envoy runs in Docker and must reach the host-run policy-engine over TCP instead of +# the shared UDS socket (there is no shared host socket in this topology). +# host.docker.internal is the Docker → host loopback; the controller reuses this +# host for BOTH the ext_proc cluster and the ALS (access-log) cluster it programs +# into Envoy (see xds.createALSCluster). +[router.policy_engine] +mode = "tcp" +host = "host.docker.internal" + +# policy-engine ext_proc gRPC server listens on TCP (default port 9001). +[policy_engine.server] +mode = "tcp" + +# Collector/ALS transport over TCP. Shared section: the controller reads it to +# configure Envoy's ALS sender, the policy-engine reads it to configure its ALS +# receiver (default port 18090). Both sides must agree, so it lives here once. +[collector.server] +mode = "tcp" + +# Python executor over TCP (Option 2B — Python policy debugging). Harmless for +# Go-only debugging (Option 2A): the bridge connects lazily, only when a Python +# policy is first triggered, so a Go-only session never dials it. +[policy_engine.python_executor.server] +mode = "tcp" +port = 9010 +host = "localhost" + +# AES-GCM at-rest encryption key. The shipped config.toml has no encryption section, +# so the host-run controller would fall back to its code-default provider path +# (./data/aesgcm-keys/…), which doesn't exist under the debug cwd → startup fails. +# Point it at the dev key instead (relative to gateway/gateway-controller, the +# controller's launch.json `cwd`). The container never loads this overlay and keeps +# using its own provisioned key, so this is debug-only. +# +# NOTE: this key file is gitignored (gateway/.gitignore) and NOT committed — it is a +# real AES-256 secret. Generate it once from the gateway/ directory before starting +# the controller: +# mkdir -p gateway-controller/aesgcm-keys +# ( umask 177; openssl rand 32 > gateway-controller/aesgcm-keys/default-aesgcm256-v1.bin ) +# See DEBUG_GUIDE.md → "Local debug config overlay". +[[controller.encryption.providers]] +type = "aesgcm" + +[[controller.encryption.providers.keys]] +version = "aesgcm256-v1" +file = "./aesgcm-keys/default-aesgcm256-v1.bin" diff --git a/gateway/examples/petstore-api.yaml b/gateway/examples/petstore-api.yaml index 665af43d0c..67682879e3 100644 --- a/gateway/examples/petstore-api.yaml +++ b/gateway/examples/petstore-api.yaml @@ -29,13 +29,8 @@ spec: context: /petstoretest upstream: main: - url: http://petstore.swagger.io/v2 + url: https://petstore.swagger.io/v2 policies: - - name: api-key-auth - version: v1 - params: - key: X-API-Key - in: header - name: set-headers version: v1 params: @@ -43,6 +38,10 @@ spec: headers: - name: X-Client-Version value: "1.2.3" + response: + headers: + - name: X-Server-Version + value: "v1.1" operations: - method: GET path: /pet/{petId} diff --git a/gateway/gateway-controller/pkg/version/version.go b/gateway/gateway-controller/pkg/version/version.go index f302c32bd7..88ad1c3c96 100644 --- a/gateway/gateway-controller/pkg/version/version.go +++ b/gateway/gateway-controller/pkg/version/version.go @@ -13,7 +13,7 @@ package version var ( - Version = "1.0.0" + Version = "1.2.0" FunctionalityType = "regular" GitCommit = "unknown" BuildDate = "unknown" From db48073046104ce00c9bdace67799120a81c7988 Mon Sep 17 00:00:00 2001 From: Renuka Fernando Date: Sun, 2 Aug 2026 14:01:48 +0530 Subject: [PATCH 2/4] chore(debug): add local-debug overlays for platform-api and BFF MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Mirror the gateway config-debug.toml pattern for the Platform API and AI Workspace BFF — a repeatable -config overlay merged last-wins over the shipped config.toml, keeping debug-only values out of the config that ships in the distribution (config.toml files left untouched): - platform-api/config/config-debug.toml: dev encryption key, admin login, host JWT keypair paths, debug logging; launch.json env emptied - ai-workspace config-debug.toml: :8081 listener + host cert/key, local control-plane URL + tls_skip_verify, debug logging - ai-workspace Makefile bff-run now loads the overlay instead of env vars, fixing the dead APIP_AIW_SERVER_* names (no config.toml token) Signed-off-by: Renuka Fernando --- .vscode/launch.json | 49 ++++++++++++++++--- platform-api/config/config-debug.toml | 44 +++++++++++++++++ portals/ai-workspace/Makefile | 24 +++------ .../ai-workspace/configs/config-debug.toml | 31 ++++++++++++ 4 files changed, 125 insertions(+), 23 deletions(-) create mode 100644 platform-api/config/config-debug.toml create mode 100644 portals/ai-workspace/configs/config-debug.toml diff --git a/.vscode/launch.json b/.vscode/launch.json index 8ad2b9fc68..4d564f7a14 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -13,6 +13,34 @@ "console": "integratedTerminal", "skipFiles": ["/**"] }, + { + // AI Workspace Go BFF on https://localhost:8081 — serves /api/*, + // proxies to the Platform API, and owns the session/OIDC flow. + // Mirrors `make bff-run`. Requires the TLS cert pair under + // resources/certificates — run `./setup.sh --certs-only` first if missing. + "name": "AI Workspace BFF", + "type": "go", + "request": "launch", + "mode": "auto", + "program": "${workspaceFolder}/portals/ai-workspace/bff", + "cwd": "${workspaceFolder}/portals/ai-workspace/bff", + "args": [ + // Shipped base config + local debug overlay (repeatable -config, last-wins). + // The overlay flips the listener to :8081, points certs at resources/, + // enables debug logging, and targets the local Platform API — config.toml + // ships untouched. + "-config", "${workspaceFolder}/portals/ai-workspace/configs/config.toml", + "-config", "${workspaceFolder}/portals/ai-workspace/configs/config-debug.toml", + "-static-dir", "${workspaceFolder}/portals/ai-workspace/dist", + ], + "env": { + // All debug-only settings (listener port/cert/key, control-plane URL + + // tls_skip_verify, log level) live in configs/config-debug.toml, loaded via + // the second -config above. Its cert/key paths are relative to this config's + // cwd (portals/ai-workspace/bff); generate the pair once with + // `./setup.sh --certs-only` (gitignored under resources/certificates). + }, + }, { "name": "Platform API", "type": "go", @@ -21,16 +49,23 @@ "program": "${workspaceFolder}/platform-api/cmd", "cwd": "${workspaceFolder}/platform-api", "args": [ + // Shipped base config + local debug overlay (repeatable -config, last-wins). + // The overlay carries every debug override — dev encryption key, admin login, + // debug logging, and the host JWT keypair paths — so config.toml ships + // untouched with its container-path defaults and no env keys. "-config", "${workspaceFolder}/platform-api/config/config.toml", + "-config", "${workspaceFolder}/platform-api/config/config-debug.toml", ], "env": { - // Database configuration - "DATABASE_DRIVER": "sqlite3", - "DATABASE_DB_PATH": "${workspaceFolder}/platform-api/data/api_platform.db", - "DATABASE_EXECUTE_SCHEMA_DDL": "true", - "DB_SCHEMA_PATH": "./internal/database/schema.sqlite.sql", - // JWT - skip signature validation in development - "JWT_SKIP_VALIDATION": "true", + // Allow the {{ file }} interpolation in config-debug.toml to read the + // host-relative dev encryption key (data/keys/encryption.key). The shipped + // default allowlist is /etc/platform-api only; this widens it to data/keys + // (relative to the platform-api/ cwd above). Generate the key once with: + // openssl rand -hex 32 > data/keys/encryption.key (gitignored) + "APIP_CONFIG_FILE_SOURCE_ALLOWLIST": "data/keys", + // Other debug overrides (admin login, debug logging, JWT keypair paths) + // live in config/config-debug.toml; generate the JWT keys via + // platform-api/scripts/setup.sh. }, }, { diff --git a/platform-api/config/config-debug.toml b/platform-api/config/config-debug.toml new file mode 100644 index 0000000000..1d674bce21 --- /dev/null +++ b/platform-api/config/config-debug.toml @@ -0,0 +1,44 @@ +# ── Local host-process debug overlay (Platform API) ─────────────────────────── +# Layered ON TOP of config/config.toml via a second `-config` flag in +# .vscode/launch.json. `-config` is repeatable and the loader merges files in order +# with last-wins precedence per key, so these values override the shipped defaults +# while leaving every other key untouched. +# +# Purpose: keep debug-only values OUT of config.toml, which ships in the distribution +# and is mounted into the container as-is. This file is loaded ONLY by the host-run +# Platform API while debugging — it is never mounted into the container. +# +# Scope: debug-only values, plus the dev file paths a host-run process needs (the +# shipped config.toml points these at container locations under /etc/platform-api). +# File paths here are relative to this config's cwd — the platform-api/ directory set +# in launch.json. Generate the JWT keypair with platform-api/scripts/setup.sh; it +# lives under platform-api/data/keys, which is gitignored. + +[platform_api] + +[platform_api.logging] +level = "debug" + +[platform_api.security] +# Dev-only key, generated once and gitignored (not committed) — same pattern as the +# JWT keypair below: openssl rand -hex 32 > data/keys/encryption.key +# The shipped config.toml sources this from a mounted file whose file-source allowlist +# is /etc/platform-api only, so a host-run process points at a host-relative file and +# launch.json widens APIP_CONFIG_FILE_SOURCE_ALLOWLIST to data/keys so the interpolator +# may read it. Path is relative to this config's cwd (platform-api/, set in launch.json). +encryption_key = "{{ file \"./data/keys/encryption.key\" }}" + +[platform_api.auth.jwt] +# Shipped config.toml points at /etc/platform-api/keys/* (container paths). Override +# with the host dev keypair, relative to the platform-api/ cwd. Generate it with +# platform-api/scripts/setup.sh (gitignored under platform-api/data/keys). +public_key_file = "./data/keys/jwt_public.pem" +private_key_file = "./data/keys/jwt_private.pem" + +# Dev admin login (admin / admin). This replaces config.toml's env-driven user entry; +# koanf replaces arrays wholesale on merge, so the roles must be repeated here. +# password_hash below is bcrypt of "admin". +[[platform_api.auth.file.users]] +username = "admin" +password_hash = "$2y$10$phptp63TInVnKh5d1RtQiuZ8W1E0BR1PQWAcpL6tXIgJE9Fz0aC1q" +roles = ["ap_admin"] diff --git a/portals/ai-workspace/Makefile b/portals/ai-workspace/Makefile index f1482a67cd..cf051fedd2 100644 --- a/portals/ai-workspace/Makefile +++ b/portals/ai-workspace/Makefile @@ -30,8 +30,6 @@ IMAGE_NAME := $(DOCKER_REGISTRY)/ai-workspace # BFF (Backend-for-Frontend) — Go server that serves the SPA, proxies all # browser→backend traffic, and owns authentication. BFF_DIR := bff -BFF_DEV_PORT ?= 8081 -CONTROL_PLANE_URL ?= https://localhost:9243 help: ## Show this help message @echo 'AI Workspace Build System (Version: $(VERSION))' @@ -49,22 +47,16 @@ run: ## Start AI Workspace dev server locally (installs deps if needed) bff-build: ## Build the BFF binary cd $(BFF_DIR) && GOWORK=off CGO_ENABLED=0 go build -o ../target/ai-workspace-bff . -# Runs the deployed config — configs/config.toml, the same file the container mounts — -# with the container-shaped defaults pointed at this machine. Each variable below is -# read by that key's '{{ env "APIP_AIW_..." }}' token in the file; the token is the only -# thing that lets an environment variable reach a key, so a key absent from the file -# cannot be set here. To test against an IDP, add APIP_AIW_AUTH_MODE=oidc and the -# APIP_AIW_OIDC_* variables the [oidc] tokens name — see README.md. -bff-run: ## Run the BFF locally (proxies to CONTROL_PLANE_URL; pair with `make run`) +# Runs the shipped configs/config.toml (the file the container mounts) layered with +# configs/config-debug.toml — a local-debug overlay that points the container-shaped +# defaults (listener :8081 + host cert/key, control-plane URL, debug logging) at this +# machine. -config is repeatable and merged last-wins, so config.toml stays untouched. +# This is the same overlay the VS Code "AI Workspace BFF" launch config uses. To test +# against an IDP, add the [ai_workspace.auth] / [oidc] keys to the overlay — see README.md. +bff-run: ## Run the BFF locally against the local Platform API (pair with `make run`) ../scripts/setup.sh --certs-only cd $(BFF_DIR) && \ - APIP_AIW_CONTROL_PLANE_URL=$(CONTROL_PLANE_URL) \ - APIP_AIW_CONTROL_PLANE_TLS_SKIP_VERIFY=true \ - APIP_AIW_SERVER_HTTPS_CERT_FILE=../resources/certificates/cert.pem \ - APIP_AIW_SERVER_HTTPS_KEY_FILE=../resources/certificates/key.pem \ - APIP_AIW_SERVER_HTTPS_PORT=$(BFF_DEV_PORT) \ - APIP_AIW_LOGGING_LEVEL=debug \ - go run . -config ../configs/config.toml -static-dir ../dist + go run . -config ../configs/config.toml -config ../configs/config-debug.toml -static-dir ../dist bff-test: ## Run BFF unit tests cd $(BFF_DIR) && GOWORK=off go test ./... diff --git a/portals/ai-workspace/configs/config-debug.toml b/portals/ai-workspace/configs/config-debug.toml new file mode 100644 index 0000000000..61a0f2e03e --- /dev/null +++ b/portals/ai-workspace/configs/config-debug.toml @@ -0,0 +1,31 @@ +# ── Local host-process debug overlay (AI Workspace BFF) ─────────────────────── +# Layered ON TOP of configs/config.toml via a second `-config` flag in +# .vscode/launch.json. `-config` is repeatable and the loader merges files in order +# with last-wins precedence per key, so these values override the shipped defaults +# while leaving every other key untouched. +# +# Purpose: keep debug-only values OUT of config.toml, which ships in the distribution +# and is mounted into the container as-is. This file is loaded ONLY by the host-run +# BFF while debugging — it is never mounted into the container. + +[ai_workspace] + +[ai_workspace.logging] +level = "debug" + +# The BFF's own HTTPS listener. In dev the Vite server (:9643) proxies /api, /proxy, +# etc. to https://localhost:8081 (see vite.config.ts BFF_DEV_TARGET), so the BFF must +# listen on 8081 — the shipped config.toml defaults to 9643, which would collide with +# Vite. cert_file/key_file are relative to this config's cwd (portals/ai-workspace/bff), +# i.e. portals/ai-workspace/resources/certificates — generate them once with +# `./setup.sh --certs-only` (gitignored; not committed). +[ai_workspace.server.https] +port = 8081 +cert_file = "../resources/certificates/cert.pem" +key_file = "../resources/certificates/key.pem" + +# Talk to the locally-run Platform API over its self-signed dev cert. tls_skip_verify +# short-circuits ca_file loading (proxy/transport.go), so no CA path is needed on the host. +[ai_workspace.control_plane] +url = "https://localhost:9243" +tls_skip_verify = true From ea129cf4e349632f40998e0fd266294c6df7d717 Mon Sep 17 00:00:00 2001 From: Renuka Fernando Date: Fri, 7 Aug 2026 13:36:49 +0530 Subject: [PATCH 3/4] docs(gateway): restructure debug guide and refresh examples - Group Options 2A/2B under an "Option 2" umbrella with shared config-overlay and one-time file-provisioning subsections, and fix the internal anchor links to match the renamed headings. - Document creating an empty api-platform.env, fixing the missing env_file startup error on `docker compose up gateway-runtime`. - Switch the generic deploy/test examples to reading-list-v1.json (JSON content type) and its /reading-list/books route. - Add an X-Served-By response header to the example's set-headers policy and use `curl -v` so the injected header is visible. Signed-off-by: Renuka Fernando --- gateway/DEBUG_GUIDE.md | 98 ++++++++++++++++----------- gateway/examples/reading-list-v1.json | 10 ++- 2 files changed, 69 insertions(+), 39 deletions(-) diff --git a/gateway/DEBUG_GUIDE.md b/gateway/DEBUG_GUIDE.md index ebfadda67c..c11287232b 100644 --- a/gateway/DEBUG_GUIDE.md +++ b/gateway/DEBUG_GUIDE.md @@ -5,8 +5,8 @@ Three debug options are available: | Option | What runs locally | Best for | |--------|------------------|----------| | **[Option 1 — Remote Debug](#option-1-recommended-remote-debug--all-components-in-docker)** *(recommended)* | Nothing — everything runs in Docker, VS Code attaches via dlv | Production-like debugging, Go policies only | -| **[Option 2A — Local Process (Go only)](#option-2a-local-process-debug--controller--policy-engine-in-vs-code)** | Controller + Policy Engine | Go policy development and iteration | -| **[Option 2B — Local Process (Go + Python)](#option-2b-local-process-debug--controller--policy-engine--python-executor-in-vs-code)** | Controller + Policy Engine + Python Executor | Python policy development and debugging | +| **[Option 2A — Local Process (Go only)](#option-2a-go-only)** | Controller + Policy Engine | Go policy development and iteration | +| **[Option 2B — Local Process (Go + Python)](#option-2b-go-and-python)** | Controller + Policy Engine + Python Executor | Python policy development and debugging | > [!TIP] > **Choose Option 2B** if you are developing or debugging a Python policy and need breakpoints, print statements, or rapid iteration without rebuilding Docker images. It extends Option 2A with the Python Executor running on the host. @@ -100,11 +100,12 @@ By default `build.yaml` uses `gomodule:` entries — policies compile from the G ```bash # Deploy a test API curl -X POST http://localhost:9090/api/management/v0.9/rest-apis \ - -H "Content-Type: application/yaml" \ - --data-binary @path/to/api.yaml + -H "Content-Type: application/json" \ + --data-binary @examples/reading-list-v1.json # Send a request through the router -curl http://localhost:8080/petstore/v1/pets +# (-v prints the response headers, incl. the X-Served-By header the API's set-headers policy adds) +curl -v http://localhost:8080/reading-list/books ``` ### Notes @@ -116,7 +117,11 @@ curl http://localhost:8080/petstore/v1/pets --- -## Local debug config overlay +## Option 2: Local Process Debug + +Two variants run the Gateway Controller and Policy Engine as local VS Code processes — only the Envoy Router stays in Docker Compose. **Option 2A** covers Go policy work; **Option 2B** extends it by also running the Python Executor on the host for Python policy work. Both variants share the config overlay and one-time file provisioning described first, so read those two sections before jumping to 2A or 2B. + +### Shared setup: config overlay Both local-process options (2A and 2B) need a handful of settings that differ from a production container — chiefly the policy-engine ext_proc, ALS, and Python-executor connections switching from **UDS to TCP**, because there is no shared Unix socket between the Docker router and the host-run processes. @@ -139,9 +144,11 @@ Why an overlay instead of editing `config.toml` or setting env vars: Workspace-relative **file paths** (policy definitions, DB) stay as `launch.json` env vars — TOML can't expand VS Code's `${workspaceFolder}` — and the Controller's `cwd` is set to `gateway/gateway-controller` so `config.toml`'s relative defaults (certs, lua, LLM templates) resolve to the checked-in dirs there. The **one file the overlay does carry** is the AES-GCM encryption key (`[[controller.encryption.providers]]`): the shipped `config.toml` has no encryption section, so without it the host-run controller falls back to a code-default key path that doesn't exist under the debug `cwd` and fails to start. Its value is a stable, repo-relative path (not per-developer), so it lives in the overlay rather than an env var. -### One-time key provisioning +### Shared setup: one-time file provisioning -The AES-256 at-rest encryption key the overlay points at is **gitignored and not committed** (it's a real secret — see `gateway/.gitignore`). The listener TLS cert/key are already committed, so this key is the only file you need to generate. Create it once from the `gateway/` directory: +Two files that both local-process options depend on are **gitignored and not committed** (see `gateway/.gitignore`), so they don't exist on a fresh checkout. Generate them once from the `gateway/` directory — the listener TLS cert/key are already committed, so these are the only files you need to create. + +**1. AES-256 at-rest encryption key** — a real secret the config overlay points at: ```bash cd gateway @@ -151,16 +158,25 @@ mkdir -p gateway-controller/aesgcm-keys This writes the 32-byte key to exactly the path the debug controller's `cwd` reads. Without it the Gateway Controller **exits at startup** while loading encryption providers. +**2. Environment file** — `docker-compose.yaml` declares `api-platform.env` as a `required` `env_file` for the containerized services. In the debug flow it carries no values you need (every key `config.toml` reads has a default), so an **empty file** is enough — the file just has to exist: + +```bash +cd gateway +touch api-platform.env +``` + +Without it, `docker compose up gateway-runtime` **fails to start** with `env file .../api-platform.env not found`. Because the file is gitignored, creating it leaves nothing to revert. + --- -## Option 2A: Local Process Debug — Controller + Policy Engine in VS Code +### Option 2A: Go only Gateway Controller and Policy Engine run as local VS Code processes. Only the Envoy Router runs in Docker Compose. > [!WARNING] > Processes run directly on the host, so Go resolves modules via `go.work`. Local versions of `sdk` and other workspace modules are used instead of the published Go module versions — including any uncommitted or untagged changes. Behavior may differ from a production build. -### Architecture +#### Architecture ```mermaid graph TB @@ -179,20 +195,20 @@ graph TB GC -->|localhost:18001| PE ``` -### Prerequisites +#### Prerequisites - VS Code with Go extension installed - Docker and Docker Compose - Control plane host and registration token (optional, for gateway registration) -- **One-time key provisioning** — generate the gitignored AES-256 encryption key the controller loads (see [One-time key provisioning](#one-time-key-provisioning)). The controller **won't start** without it. +- **One-time file provisioning** — generate the gitignored AES-256 encryption key and the empty `api-platform.env` (see [Shared setup: one-time file provisioning](#shared-setup-one-time-file-provisioning)). The controller **won't start**, and `docker compose up` **fails**, without them. -### Step 1: Run Gateway Builder +#### Step 1: Run Gateway Builder Run the **Gateway Builder** debug configuration from VS Code. This compiles all policies and generates the policy-engine binary into `gateway/gateway-builder/target/output/`. It also writes the policy **definition** files into `gateway/gateway-builder/target/output/gateway-controller/policies/` — the directory the controller reads via `APIP_GW_CONTROLLER_POLICIES_DEFINITIONS_PATH`. > **Note:** This is the slowest step, so start it first — it can compile in the background while you do Steps 2–3. It **must** finish before you start the Gateway Controller (Step 4): the controller loads these policy definitions once at startup (before it hydrates stored configs and builds the first xDS snapshot). If the directory is still empty because the builder hasn't finished, the controller starts with **zero** policy definitions — with no hard error — and you must restart it after the builder completes. -### Step 2: Configure Control Plane Connection +#### Step 2: Configure Control Plane Connection Update `.vscode/launch.json` in the **Gateway Controller** configuration with your control plane details: @@ -213,7 +229,7 @@ Update `.vscode/launch.json` in the **Gateway Controller** configuration with yo > and the matching token for the registration token). Leave them empty (`""`) to run in standalone > mode without a control plane connection. -### Step 3: Update Docker Compose Configuration +#### Step 3: Update Docker Compose Configuration In `gateway/docker-compose.yaml`, make two changes to the `gateway-runtime` service: @@ -244,15 +260,15 @@ services: # - "9003:9003" # Metrics ``` -### Step 4: Start Gateway Controller +#### Step 4: Start Gateway Controller Run the **Gateway Controller** debug configuration from VS Code. -### Step 5: Start Policy Engine +#### Step 5: Start Policy Engine Run the **Policy Engine - xDS** debug configuration from VS Code. -### Step 6: Start Gateway Runtime (Router) +#### Step 6: Start Gateway Runtime (Router) Run the router in Docker Compose: @@ -262,26 +278,32 @@ docker compose up gateway-runtime sample-backend -d docker compose logs -ft gateway-runtime sample-backend ``` -### Step 7: Deploy an API and Test +#### Step 7: Deploy an API and Test Deploy a test API via the Gateway Controller REST API. The `-u "admin:admin"` below is the **local-only** debug login set by the `Gateway Controller` launch config (`config-debug.toml`); it is never a shipped or deployable credential. Against a real control plane, pass your own instead, e.g. `-u "$APIP_ADMIN_USER:$APIP_ADMIN_PASS"`. ```bash curl -X POST http://localhost:9090/api/management/v1/rest-apis \ - -H "Content-Type: application/yaml" \ + -H "Content-Type: application/json" \ -u "admin:admin" \ - --data-binary @examples/petstore-api.yaml + --data-binary @examples/reading-list-v1.json ``` -Send a request to the deployed API: +Send a request to the deployed API. The `-v` flag prints the response headers, so you can confirm the `X-Served-By` header injected by the API's `set-headers` policy: ```bash -curl http://localhost:8080/petstoretest/pet/85964529 +curl -v http://localhost:8080/reading-list/books +``` + +In the verbose output you should see the gateway-added response header: + +```text +< X-Served-By: wso2 api platform gateway ``` --- -## Option 2B: Local Process Debug — Controller + Policy Engine + Python Executor in VS Code +### Option 2B: Go and Python This extends **Option 2A** by also running the Python Executor on the host, giving you full debugger access to the Python policy runtime. @@ -291,7 +313,7 @@ This extends **Option 2A** by also running the Python Executor on the host, givi > [!WARNING] > Processes run directly on the host, so Go resolves modules via `go.work`. Local versions of `sdk` and other workspace modules are used instead of the published Go module versions — including any uncommitted or untagged changes. Behavior may differ from a production build. -### Architecture +#### Architecture ```mermaid graph TB @@ -311,15 +333,15 @@ graph TB PE -->|"localhost:9010"| PYE ``` -### Prerequisites +#### Prerequisites - Python 3.10+ with `venv` - VS Code with Go and Python extensions installed - Docker and Docker Compose - Control plane host and registration token (optional, for gateway registration) -- **One-time key provisioning** — generate the gitignored AES-256 encryption key the controller loads (see [One-time key provisioning](#one-time-key-provisioning)). The controller **won't start** without it. +- **One-time file provisioning** — generate the gitignored AES-256 encryption key and the empty `api-platform.env` (see [Shared setup: one-time file provisioning](#shared-setup-one-time-file-provisioning)). The controller **won't start**, and `docker compose up` **fails**, without them. -### Step 1: TCP Mode (no action needed) +#### Step 1: TCP Mode (no action needed) The Policy Engine reaches the host-run Python Executor over TCP at `localhost:9010`. This is **already configured** in the local debug overlay `configs/config-debug.toml`, which the **Policy Engine - xDS** launch config loads as a second `-config` on top of `config.toml`: @@ -333,9 +355,9 @@ host = "localhost" No `config.toml` edit is required, and there is nothing to revert afterward. Because the overlay is passed only on the launch config's command line — never mounted into the container — the shipped `config.toml` stays in UDS mode, so the containerized Policy Engine is unaffected. -> **How it works:** `-config` is repeatable; the loaders merge the files in order with last-wins precedence, so the overlay's `tcp` values override the `uds` defaults from `config.toml` for the host-run process only. See [Local debug config overlay](#local-debug-config-overlay). +> **How it works:** `-config` is repeatable; the loaders merge the files in order with last-wins precedence, so the overlay's `tcp` values override the `uds` defaults from `config.toml` for the host-run process only. See [Shared setup: config overlay](#shared-setup-config-overlay). -### Step 2: Run Gateway Builder +#### Step 2: Run Gateway Builder Run the **Gateway Builder** debug configuration from VS Code. This compiles all policies (Go + Python) and generates: @@ -345,7 +367,7 @@ Run the **Gateway Builder** debug configuration from VS Code. This compiles all > **Note:** Wait for the builder to complete successfully before starting the other components. -### Step 3: Prepare the Python Environment +#### Step 3: Prepare the Python Environment ```bash cd gateway @@ -365,7 +387,7 @@ cp gateway-builder/target/output/python-executor/python_policy_registry.py \ > [!IMPORTANT] > Re-run the `pip install` and `cp` steps after every builder run if policies change. -### Step 4: Update Docker Compose Configuration +#### Step 4: Update Docker Compose Configuration In `gateway/docker-compose.yaml`, make two changes to the `gateway-runtime` service: @@ -397,13 +419,13 @@ services: ``` -### Step 5: Start Gateway Controller +#### Step 5: Start Gateway Controller Run the **Gateway Controller** debug configuration from VS Code. > **Note:** Leave `APIP_GW_CONTROLLER_CONTROLPLANE_HOST` and `APIP_GW_CONTROLLER_CONTROLPLANE_TOKEN` empty (`""`) in `.vscode/launch.json` if you want to run in standalone mode without control plane connection. -### Step 6: Start the Python Executor +#### Step 6: Start the Python Executor Run the **Python Executor** configuration from VS Code (see [Python debugging tips](#python-debugging-tips) below for breakpoint locations). @@ -426,7 +448,7 @@ Loaded policy factory: prompt-compressor:v0 from prompt_compressor_v0.policy Python Executor ready on localhost:9010 ``` -### Step 7: Start the Policy Engine +#### Step 7: Start the Policy Engine Run the **Policy Engine - xDS** debug configuration from VS Code. @@ -436,7 +458,7 @@ The Policy Engine will connect to the Python Executor over TCP when the first Py Python executor bridge initialized address=localhost:9010 mode=tcp timeout=30s ``` -### Step 8: Start the Gateway Runtime (Router) +#### Step 8: Start the Gateway Runtime (Router) Run the router in Docker Compose: @@ -446,7 +468,7 @@ docker compose up gateway-runtime sample-backend -d docker compose logs -ft gateway-runtime sample-backend ``` -### Step 9: Deploy and Test +#### Step 9: Deploy and Test ```bash # Deploy an API with a Python policy (e.g., prompt-compressor) @@ -461,7 +483,7 @@ curl -X POST http://localhost:8080/your-api/chat \ -d '{"messages": [{"role": "user", "content": "Your test prompt here"}]}' ``` -### Step 10: Clean Up +#### Step 10: Clean Up When you are done debugging, **revert the Docker Compose changes** from Step 4 (restore `GATEWAY_CONTROLLER_HOST` and uncomment the Policy Engine ports) so `docker compose up` runs the full containerized stack again. diff --git a/gateway/examples/reading-list-v1.json b/gateway/examples/reading-list-v1.json index fa95e5f448..d86cd276bf 100644 --- a/gateway/examples/reading-list-v1.json +++ b/gateway/examples/reading-list-v1.json @@ -10,7 +10,7 @@ "context": "/reading-list", "upstream": { "main": { - "url": "https://httpbin.org/anything/my-books" + "url": "https://apis.bijira.dev/samples/reading-list-api-service/v1.0" } }, "policies": [ @@ -25,6 +25,14 @@ "value": "wso2 api platform gateway" } ] + }, + "response": { + "headers": [ + { + "name": "X-Served-By", + "value": "wso2 api platform gateway" + } + ] } } } From 91e0d9b2fe46391eb122b78bfaa75bd2e5d57be7 Mon Sep 17 00:00:00 2001 From: Renuka Fernando Date: Fri, 7 Aug 2026 14:01:53 +0530 Subject: [PATCH 4/4] docs(gateway): harden debug guide auth and key provisioning Address CodeRabbit review findings on the debug guide: - Add `-u "admin:admin"` (and switch to the v1 management API) on the Option 1 deploy example so it doesn't 401 when the controller requires Basic Auth. - Guard the AES-GCM key provisioning against overwriting an existing key (refuse if present, use noclobber, chmod 600) so rerunning it can't silently make already-encrypted data undecryptable. Signed-off-by: Renuka Fernando --- gateway/DEBUG_GUIDE.md | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/gateway/DEBUG_GUIDE.md b/gateway/DEBUG_GUIDE.md index c11287232b..c373989c8a 100644 --- a/gateway/DEBUG_GUIDE.md +++ b/gateway/DEBUG_GUIDE.md @@ -99,8 +99,9 @@ By default `build.yaml` uses `gomodule:` entries — policies compile from the G ```bash # Deploy a test API -curl -X POST http://localhost:9090/api/management/v0.9/rest-apis \ +curl -X POST http://localhost:9090/api/management/v1/rest-apis \ -H "Content-Type: application/json" \ + -u "admin:admin" \ --data-binary @examples/reading-list-v1.json # Send a request through the router @@ -153,11 +154,19 @@ Two files that both local-process options depend on are **gitignored and not com ```bash cd gateway mkdir -p gateway-controller/aesgcm-keys -( umask 177; openssl rand 32 > gateway-controller/aesgcm-keys/default-aesgcm256-v1.bin ) +key_path="gateway-controller/aesgcm-keys/default-aesgcm256-v1.bin" +if [ -e "$key_path" ]; then + echo "Refusing to overwrite existing AES-GCM key: $key_path" >&2 + exit 1 +fi +( umask 177; set -o noclobber; openssl rand 32 > "$key_path" ) +chmod 600 "$key_path" ``` This writes the 32-byte key to exactly the path the debug controller's `cwd` reads. Without it the Gateway Controller **exits at startup** while loading encryption providers. +> The guard refuses to overwrite an existing key: it's provisioned **once** per checkout. Rerunning after the controller has encrypted stored data would replace the key and make that data undecryptable — rotate keys only through an explicit migration, never by regenerating this file. + **2. Environment file** — `docker-compose.yaml` declares `api-platform.env` as a `required` `env_file` for the containerized services. In the debug flow it carries no values you need (every key `config.toml` reads has a default), so an **empty file** is enough — the file just has to exist: ```bash