Skip to content

fix: clarify APIGateway rate limiter is instance-scoped, not global#103

Merged
intel352 merged 7 commits intomainfrom
fix/api-gateway-rate-limiter-isolation
Feb 23, 2026
Merged

fix: clarify APIGateway rate limiter is instance-scoped, not global#103
intel352 merged 7 commits intomainfrom
fix/api-gateway-rate-limiter-isolation

Conversation

@intel352
Copy link
Contributor

Summary

  • Rename globalLimiter field → instanceRateLimiter to clearly convey that state is per-instance, not process-wide
  • Rename SetGlobalRateLimitSetRateLimit; keep SetGlobalRateLimit as a deprecated wrapper so existing callers are not broken
  • Add APIGatewayOption type + WithRateLimit(cfg) functional option for clean dependency injection at construction time
  • Document on the APIGateway struct that rate limiter state is never shared across instances

Closes #61

Test plan

  • TestAPIGateway_InstanceRateLimit_SetRateLimit — existing rate-limit behavior still works via the renamed setter
  • TestAPIGateway_InstanceRateLimit_WithRateLimit — new constructor option enforces the limit correctly
  • TestAPIGateway_InstanceRateLimiters_AreIsolated — exhausting one gateway's burst has zero effect on a second gateway instance (proves isolation)
  • All existing tests continue to pass: go test ./...
  • golangci-lint run ./... — 0 issues

🤖 Generated with Claude Code

intel352 and others added 4 commits February 22, 2026 23:23
#95)

Verified that all JWT validation paths in JWTAuthModule already enforce
HS256 via both type assertion (*jwt.SigningMethodHMAC) and explicit
algorithm check (token.Method.Alg() != jwt.SigningMethodHS256.Alg()).

Added tests to module/jwt_auth_test.go that explicitly confirm tokens
signed with HS384 or HS512 are rejected by:
- Authenticate() — the AuthProvider interface method
- handleRefresh via Handle() — the /auth/refresh endpoint
- extractUserFromRequest via Handle() — all protected endpoints

The api package (middleware.go, auth_handler.go) already had equivalent
algorithm rejection tests in auth_handler_test.go.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
 #66)

Adds detailed documentation for audit logging, license.validator,
platform.provider/resource/context, observability.otel, step.jq, AI
pipeline steps (ai_complete, ai_classify, ai_extract), CI/CD steps
(docker_build, docker_push, docker_run, scan_sast, scan_container,
scan_deps, artifact_push, artifact_pull), and the admincore plugin.
Each entry includes config field tables with types and defaults, plus
a minimal YAML example. Summary tables in the module type index are
also updated with the new Infrastructure and CI/CD Step categories.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add dedicated rate-limit middleware modules for login (10/min) and
  register (5/hour) in admin/config.yaml, replacing the shared global
  limiter that allowed 120 req/min on sensitive auth endpoints
- Extend RateLimitMiddleware to support fractional rates via float64
  token buckets, enabling hourly rate configs without precision loss
- Add NewRateLimitMiddlewareWithHourlyRate constructor and factory
  support for requestsPerHour config key
- Add requestsPerHour field to the http.middleware.ratelimit schema
- Add tests for hourly rate middleware and per-hour factory config

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…loses #61)

- Rename field globalLimiter -> instanceRateLimiter to make scope clear
- Rename SetGlobalRateLimit -> SetRateLimit; keep SetGlobalRateLimit as
  a deprecated alias so existing callers continue to work
- Add APIGatewayOption + WithRateLimit() functional option for DI at
  construction time (preferred over the setter)
- Document on the struct that rate limiter state is never shared across
  instances, so multi-tenant deployments are not affected
- Add TestAPIGateway_InstanceRateLimit_WithRateLimit and
  TestAPIGateway_InstanceRateLimiters_AreIsolated to cover the new
  option and prove per-instance isolation

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings February 23, 2026 04:45
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This pull request refactors the APIGateway rate limiter naming and API to clarify that rate limiting is instance-scoped, not global. It addresses issue #61 by renaming globalLimiterinstanceRateLimiter, deprecating SetGlobalRateLimit in favor of SetRateLimit, and adding a WithRateLimit functional option for dependency injection at construction time. Additionally, the PR extends rate limiting middleware to support hourly rates (fractional per-minute rates) and adds JWT algorithm confusion attack prevention tests.

Changes:

  • Renamed APIGateway rate limiter field and methods to clarify instance-scoped behavior
  • Added functional option pattern for rate limit configuration at construction time
  • Extended rate limiting middleware to support requestsPerHour configuration
  • Added comprehensive JWT HS256 algorithm pinning tests

Reviewed changes

Copilot reviewed 10 out of 10 changed files in this pull request and generated no comments.

Show a summary per file
File Description
module/api_gateway.go Renamed globalLimiterinstanceRateLimiter, added WithRateLimit functional option, deprecated SetGlobalRateLimit, added documentation clarifying instance-scoped behavior
module/api_gateway_test.go Updated test names to reflect instance-scoped nature, added tests for WithRateLimit constructor option and instance isolation
module/http_middleware.go Added ratePerMinute float64 field, created NewRateLimitMiddlewareWithHourlyRate, updated token bucket algorithm to use fractional rates
module/http_middleware_test.go Added tests for hourly rate limiting functionality, updated existing tests to use float64 tokens
plugins/http/modules.go Extended factory to check for requestsPerHour config and call NewRateLimitMiddlewareWithHourlyRate when set
plugins/http/schemas.go Added requestsPerHour field to rate limiter schema with documentation
plugins/http/plugin_test.go Added test coverage for requestsPerHour configuration parsing (int and float64)
module/jwt_auth_test.go Added comprehensive tests verifying JWT validation rejects non-HS256 algorithms across all validation paths
admin/config.yaml Added dedicated rate limiters for login (10/min) and registration (5/hour) endpoints
DOCUMENTATION.md Added extensive module type reference documentation for various new and existing module types

intel352 and others added 2 commits February 22, 2026 23:58
Update plugins/api/plugin.go to call SetRateLimit instead of the
deprecated SetGlobalRateLimit, fixing staticcheck lint failure.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings February 23, 2026 06:28
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 10 out of 10 changed files in this pull request and generated 3 comments.

Outputs: []schema.ServiceIODef{{Name: "limited", Type: "http.Request", Description: "HTTP request (passed through if within limit)"}},
ConfigFields: []schema.ConfigFieldDef{
{Key: "requestsPerMinute", Label: "Requests Per Minute", Type: schema.FieldTypeNumber, DefaultValue: 60, Description: "Maximum number of requests per minute per client"},
{Key: "requestsPerMinute", Label: "Requests Per Minute", Type: schema.FieldTypeNumber, DefaultValue: 60, Description: "Maximum number of requests per minute per client (mutually exclusive with requestsPerHour)"},
Copy link

Copilot AI Feb 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The description states the two fields are "mutually exclusive" but the implementation in plugins/http/modules.go shows that requestsPerHour takes precedence when both are set, rather than treating them as mutually exclusive. Consider changing "mutually exclusive" to "requestsPerHour takes precedence over requestsPerMinute when both are set" for accuracy.

Suggested change
{Key: "requestsPerMinute", Label: "Requests Per Minute", Type: schema.FieldTypeNumber, DefaultValue: 60, Description: "Maximum number of requests per minute per client (mutually exclusive with requestsPerHour)"},
{Key: "requestsPerMinute", Label: "Requests Per Minute", Type: schema.FieldTypeNumber, DefaultValue: 60, Description: "Maximum number of requests per minute per client; used when requestsPerHour is not set"},

Copilot uses AI. Check for mistakes.
Comment on lines 103 to +728
@@ -115,6 +131,14 @@ Pipeline steps support Go template syntax with these built-in functions:

Template expressions can reference previous step outputs via `{{ .steps.step-name.field }}` or for hyphenated names `{{index .steps "step-name" "field"}}`.

### Infrastructure
| Type | Description |
|------|-------------|
| `license.validator` | License key validation against a remote server with caching and grace period |
| `platform.provider` | Cloud infrastructure provider declaration (e.g., Terraform, Pulumi) |
| `platform.resource` | Infrastructure resource managed by a platform provider |
| `platform.context` | Execution context for platform operations (org, environment, tier) |

### Observability
| Type | Description |
|------|-------------|
@@ -161,6 +185,548 @@ Template expressions can reference previous step outputs via `{{ .steps.step-nam
| `data.transformer` | Data transformation |
| `workflow.registry` | Workflow registration and discovery |

## Module Type Reference

Detailed configuration reference for module types not covered in the main table above.

### Audit Logging (`audit/`)

The `audit/` package provides a structured JSON audit logger for recording security-relevant events. It is used internally by the engine and admin platform -- not a YAML module type, but rather a Go library used by other modules.

**Event types:** `auth`, `auth_failure`, `admin_op`, `escalation`, `data_access`, `config_change`, `component_op`

Each audit event is written as a single JSON line containing `timestamp`, `type`, `action`, `actor`, `resource`, `detail`, `source_ip`, `success`, and `metadata` fields.

---

### `license.validator`

Validates license keys against a remote server with local caching and an offline grace period. When no `server_url` is configured the module operates in offline/starter mode and synthesizes a valid starter-tier license locally.

**Configuration:**

| Key | Type | Default | Description |
|-----|------|---------|-------------|
| `server_url` | string | `""` | License validation server URL. Leave empty for offline/starter mode. |
| `license_key` | string | `""` | License key. Supports `$ENV_VAR` expansion. Falls back to `WORKFLOW_LICENSE_KEY` env var. |
| `cache_ttl` | duration | `1h` | How long to cache a valid license result before re-validating. |
| `grace_period` | duration | `72h` | How long to allow operation when the license server is unreachable. |
| `refresh_interval` | duration | `1h` | How often the background goroutine re-validates the license. |

**Outputs:** Provides the `license-validator` service (`LicenseValidator`).

**Example:**

```yaml
modules:
- name: license
type: license.validator
config:
server_url: "https://license.gocodalone.com/api/v1"
license_key: "$WORKFLOW_LICENSE_KEY"
cache_ttl: "1h"
grace_period: "72h"
refresh_interval: "1h"
```

---

### `platform.provider`

Declares a cloud infrastructure provider (e.g., Terraform, Pulumi) for use with the platform workflow handler and reconciliation trigger.

**Configuration:**

| Key | Type | Required | Description |
|-----|------|----------|-------------|
| `name` | string | yes | Provider name used to construct the service name `platform.provider.<name>`. |

**Example:**

```yaml
modules:
- name: cloud-provider
type: platform.provider
config:
name: "aws"
```

---

### `platform.resource`

Declares an infrastructure resource managed by a platform provider. Config keys are provider-specific and passed through as-is.

**Configuration:**

| Key | Type | Required | Description |
|-----|------|----------|-------------|
| `type` | string | yes | Infrastructure resource type (e.g., `database`, `queue`, `container_runtime`). |
| *(additional keys)* | any | no | Provider-specific resource properties. |

**Example:**

```yaml
modules:
- name: orders-db
type: platform.resource
config:
type: database
engine: postgresql
storage: "10Gi"
```

---

### `platform.context`

Provides the execution context for platform operations. Used to identify the organization, environment, and tier for a deployment.

**Configuration:**

| Key | Type | Required | Description |
|-----|------|----------|-------------|
| `path` | string | yes | Path identifying this context. |
| `org` | string | no | Organization name. |
| `environment` | string | no | Deployment environment (e.g., `production`, `staging`). |
| `tier` | number | no | Platform tier level. |

**Example:**

```yaml
modules:
- name: platform-ctx
type: platform.context
config:
path: "acme-corp/production"
org: "acme-corp"
environment: "production"
tier: 3
```

---

### `observability.otel`

Initializes an OpenTelemetry distributed tracing provider that exports spans via OTLP/HTTP to a collector. Sets the global OTel tracer provider so all instrumented code in the process is covered.

**Configuration:**

| Key | Type | Default | Description |
|-----|------|---------|-------------|
| `endpoint` | string | `localhost:4318` | OTLP collector endpoint (host:port). |
| `serviceName` | string | `workflow` | Service name used for trace attribution. |

**Outputs:** Provides the `tracer` service (`trace.Tracer`).

**Example:**

```yaml
modules:
- name: tracing
type: observability.otel
config:
endpoint: "otel-collector:4318"
serviceName: "order-api"
```

---

### `step.jq`

Applies a JQ expression to pipeline data for complex transformations. Uses the `gojq` pure-Go JQ implementation, supporting the full JQ language: field access, pipes, `map`/`select`, object construction, arithmetic, conditionals, and more.

The expression is compiled at startup so syntax errors are caught early. When the result is a single object, its keys are merged into the step output so downstream steps can access fields directly.

**Configuration:**

| Key | Type | Required | Description |
|-----|------|----------|-------------|
| `expression` | string | yes | JQ expression to evaluate. |
| `input_from` | string | no | Dotted path to the input value (e.g., `steps.fetch.items`). Defaults to the full current pipeline context. |

**Output fields:** `result` — the JQ result. When the result is a single object, its keys are also promoted to the top level.

**Example:**

```yaml
steps:
- name: extract-active
type: step.jq
config:
input_from: "steps.fetch-users.users"
expression: "[.[] | select(.active == true) | {id, email}]"
```

---

### `step.ai_complete`

Invokes an AI provider to produce a text completion. Provider resolution order: explicit `provider` name, then model-based lookup, then first registered provider.

**Configuration:**

| Key | Type | Default | Description |
|-----|------|---------|-------------|
| `provider` | string | `""` | Named AI provider to use. Omit to auto-select. |
| `model` | string | `""` | Model name (e.g., `claude-3-5-sonnet-20241022`). Used for provider lookup if `provider` is unset. |
| `system_prompt` | string | `""` | System prompt. Supports Go template syntax with pipeline context. |
| `input_from` | string | `""` | Template expression to resolve the user message (e.g., `.body`). Falls back to `text` or `body` fields in current context. |
| `max_tokens` | number | `1024` | Maximum tokens in the completion. |
| `temperature` | number | `0` | Sampling temperature (0.0–1.0). |

**Output fields:** `content`, `model`, `finish_reason`, `usage.input_tokens`, `usage.output_tokens`.

**Example:**

```yaml
steps:
- name: summarize
type: step.ai_complete
config:
model: "claude-3-5-haiku-20241022"
system_prompt: "You are a helpful assistant. Summarize the following text concisely."
input_from: ".body"
max_tokens: 512
```

---

### `step.ai_classify`

Classifies input text into one of a configured set of categories using an AI provider. Returns the winning category, a confidence score (0.0–1.0), and brief reasoning.

**Configuration:**

| Key | Type | Required | Description |
|-----|------|----------|-------------|
| `categories` | array of strings | yes | List of valid classification categories. |
| `provider` | string | no | Named AI provider. Auto-selected if omitted. |
| `model` | string | no | Model name for provider lookup. |
| `input_from` | string | no | Template expression for the input text. Falls back to `text` or `body` fields. |
| `max_tokens` | number | `256` | Maximum tokens for the classification response. |
| `temperature` | number | `0` | Sampling temperature. |

**Output fields:** `category`, `confidence`, `reasoning`, `raw`, `model`, `usage.input_tokens`, `usage.output_tokens`.

**Example:**

```yaml
steps:
- name: classify-ticket
type: step.ai_classify
config:
input_from: ".body"
categories:
- "billing"
- "technical-support"
- "account"
- "general-inquiry"
```

---

### `step.ai_extract`

Extracts structured data from text using an AI provider. When the provider supports tool use, it uses the tool-calling API for reliable structured output. Otherwise it falls back to prompt-based JSON extraction.

**Configuration:**

| Key | Type | Required | Description |
|-----|------|----------|-------------|
| `schema` | object | yes | JSON Schema object describing the fields to extract. |
| `provider` | string | no | Named AI provider. Auto-selected if omitted. |
| `model` | string | no | Model name for provider lookup. |
| `input_from` | string | no | Template expression for the input text. Falls back to `text` or `body` fields. |
| `max_tokens` | number | `1024` | Maximum tokens. |
| `temperature` | number | `0` | Sampling temperature. |

**Output fields:** `extracted` (map of extracted fields), `method` (`tool_use`, `text_parse`, or `prompt`), `model`, `usage.input_tokens`, `usage.output_tokens`.

**Example:**

```yaml
steps:
- name: extract-order
type: step.ai_extract
config:
input_from: ".body"
schema:
type: object
properties:
customer_name: {type: string}
order_items: {type: array, items: {type: string}}
total_amount: {type: number}
```

---

### `step.docker_build`

Builds a Docker image from a context directory and Dockerfile using the Docker SDK. The context directory is tar-archived and sent to the Docker daemon.

**Configuration:**

| Key | Type | Required | Description |
|-----|------|----------|-------------|
| `context` | string | yes | Path to the build context directory. |
| `dockerfile` | string | `Dockerfile` | Dockerfile path relative to the context directory. |
| `tags` | array of strings | no | Image tags to apply (e.g., `["myapp:latest", "myapp:1.2.3"]`). |
| `build_args` | map | no | Build argument key/value pairs. |
| `cache_from` | array of strings | no | Image references to use as layer cache sources. |

**Output fields:** `image_id`, `tags`, `context`.

**Example:**

```yaml
steps:
- name: build-image
type: step.docker_build
config:
context: "./src"
dockerfile: "Dockerfile"
tags:
- "myapp:latest"
build_args:
APP_VERSION: "1.2.3"
```

---

### `step.docker_push`

Pushes a Docker image to a remote registry.

**Configuration:**

| Key | Type | Required | Description |
|-----|------|----------|-------------|
| `image` | string | yes | Image name/tag to push. |
| `registry` | string | no | Registry hostname prefix (prepended to `image` when constructing the reference). |
| `auth_provider` | string | no | Named auth provider for registry credentials (informational; credentials are read from Docker daemon config). |

**Output fields:** `image`, `registry`, `digest`, `auth_provider`.

**Example:**

```yaml
steps:
- name: push-image
type: step.docker_push
config:
image: "myapp:latest"
registry: "ghcr.io/myorg"
```

---

### `step.docker_run`

Runs a command inside a Docker container using the sandbox. Returns exit code, stdout, and stderr.

**Configuration:**

| Key | Type | Required | Description |
|-----|------|----------|-------------|
| `image` | string | yes | Docker image to run. |
| `command` | array of strings | no | Command to execute inside the container. Uses image default entrypoint if omitted. |
| `env` | map | no | Environment variables to set in the container. |
| `wait_for_exit` | boolean | `true` | Whether to wait for the container to exit. |
| `timeout` | duration | `""` | Maximum time to wait for the container. |

**Output fields:** `exit_code`, `stdout`, `stderr`, `image`.

**Example:**

```yaml
steps:
- name: run-tests
type: step.docker_run
config:
image: "golang:1.25"
command: ["go", "test", "./..."]
env:
CI: "true"
timeout: "10m"
```

---

### `step.scan_sast`

Runs a Static Application Security Testing (SAST) scanner inside a Docker container and evaluates findings against a severity gate. Supports Semgrep and generic scanner commands.

**Configuration:**

| Key | Type | Required | Description |
|-----|------|----------|-------------|
| `scanner` | string | yes | Scanner to use. Supported: `semgrep`. Generic commands also accepted. |
| `image` | string | `semgrep/semgrep:latest` | Docker image for the scanner. |
| `source_path` | string | `/workspace` | Path to the source code to scan. |
| `rules` | array of strings | no | Semgrep rule configs to apply (e.g., `auto`, `p/owasp-top-ten`). |
| `fail_on_severity` | string | `error` | Minimum severity that causes the step to fail (`error`, `warning`, `info`). |
| `output_format` | string | `sarif` | Output format: `sarif` or `json`. |

**Output fields:** `scan_result`, `command`, `image`.

**Example:**

```yaml
steps:
- name: sast-scan
type: step.scan_sast
config:
scanner: "semgrep"
source_path: "/workspace/src"
rules:
- "p/owasp-top-ten"
- "p/golang"
fail_on_severity: "error"
```

---

### `step.scan_container`

Scans a container image for vulnerabilities using Trivy. Evaluates findings against a configurable severity threshold.

**Configuration:**

| Key | Type | Required | Description |
|-----|------|----------|-------------|
| `target_image` | string | yes | Container image to scan (e.g., `myapp:latest`). |
| `scanner` | string | `trivy` | Scanner to use. |
| `severity_threshold` | string | `HIGH` | Minimum severity to report: `CRITICAL`, `HIGH`, `MEDIUM`, `LOW`, or `INFO`. |
| `ignore_unfixed` | boolean | `false` | Skip vulnerabilities without a known fix. |
| `output_format` | string | `sarif` | Output format: `sarif` or `json`. |

**Output fields:** `scan_result`, `command`, `image`, `target_image`.

**Example:**

```yaml
steps:
- name: scan-image
type: step.scan_container
config:
target_image: "myapp:latest"
severity_threshold: "HIGH"
ignore_unfixed: true
```

---

### `step.scan_deps`

Scans project dependencies for known vulnerabilities using Grype. Evaluates findings against a severity gate.

**Configuration:**

| Key | Type | Required | Description |
|-----|------|----------|-------------|
| `scanner` | string | `grype` | Scanner to use. |
| `image` | string | `anchore/grype:latest` | Docker image for the scanner. |
| `source_path` | string | `/workspace` | Path to the project source to scan. |
| `fail_on_severity` | string | `high` | Minimum severity that causes the step to fail: `critical`, `high`, `medium`, `low`, or `info`. |
| `output_format` | string | `sarif` | Output format: `sarif` or `json`. |

**Output fields:** `scan_result`, `command`, `image`.

**Example:**

```yaml
steps:
- name: dep-scan
type: step.scan_deps
config:
source_path: "/workspace"
fail_on_severity: "high"
```

---

### `step.artifact_push`

Reads a file from `source_path` and stores it in the pipeline's artifact store. Computes a SHA-256 checksum of the artifact. Requires `artifact_store` and `execution_id` in pipeline metadata.

**Configuration:**

| Key | Type | Required | Description |
|-----|------|----------|-------------|
| `source_path` | string | yes | Path to the file to store. |
| `key` | string | yes | Artifact key under which to store the file. |
| `dest` | string | `artifact_store` | Destination identifier (informational). |

**Output fields:** `key`, `size`, `checksum`, `dest`.

**Example:**

```yaml
steps:
- name: upload-binary
type: step.artifact_push
config:
source_path: "./bin/server"
key: "server-binary"
```

---

### `step.artifact_pull`

Retrieves an artifact from a prior execution, a URL, or S3 and writes it to a local destination path.

**Configuration:**

| Key | Type | Required | Description |
|-----|------|----------|-------------|
| `source` | string | yes | Source type: `previous_execution`, `url`, or `s3`. |
| `dest` | string | yes | Local file path to write the artifact to. |
| `key` | string | yes (for `previous_execution`, `s3`) | Artifact key to retrieve. |
| `execution_id` | string | no | Specific execution ID to pull from. Defaults to current execution. |
| `url` | string | yes (for `url`) | URL to fetch the artifact from. |

**Output fields:** `source`, `key`, `dest`, `size`, `bytes_written`.

**Example:**

```yaml
steps:
- name: download-binary
type: step.artifact_pull
config:
source: "previous_execution"
key: "server-binary"
dest: "./bin/server"
```

---

### Admin Core Plugin (`plugin/admincore/`)

The `admincore` plugin is a NativePlugin that registers the built-in admin UI page definitions. It declares no HTTP routes -- all views are rendered entirely in the React frontend. Registering this plugin ensures navigation is driven by the plugin system with no static fallbacks.

**UI pages declared:**

| ID | Label | Category |
|----|-------|----------|
| `dashboard` | Dashboard | global |
| `editor` | Editor | global |
| `marketplace` | Marketplace | global |
| `templates` | Templates | global |
| `environments` | Environments | global |
| `settings` | Settings | global |
| `executions` | Executions | workflow |
| `logs` | Logs | workflow |
| `events` | Events | workflow |

Global pages appear in the main navigation. Workflow-scoped pages (`executions`, `logs`, `events`) are only shown when a workflow is open.

The plugin is auto-registered via `init()` in `plugin/admincore/plugin.go`. No YAML configuration is required.

---
Copy link

Copilot AI Feb 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This PR adds extensive documentation for many module types (JQ, AI steps, Docker steps, scanning steps, license validator, platform resources, etc.) that are not related to the rate limiting clarification described in the PR title and description. While the documentation additions appear accurate, they significantly expand the scope of this PR beyond what's described. Consider whether this documentation should be in a separate PR focused on documentation improvements, to keep this PR focused on the rate limiter naming and API changes.

Copilot uses AI. Check for mistakes.
{Key: "requestsPerHour", Label: "Requests Per Hour", Type: schema.FieldTypeNumber, DefaultValue: 0, Description: "Maximum number of requests per hour per client; takes precedence over requestsPerMinute when set"},
{Key: "burstSize", Label: "Burst Size", Type: schema.FieldTypeNumber, DefaultValue: 10, Description: "Maximum burst of requests allowed above the rate limit"},
},
DefaultConfig: map[string]any{"requestsPerMinute": 60, "burstSize": 10},
Copy link

Copilot AI Feb 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The DefaultConfig only includes requestsPerMinute and burstSize, but does not include requestsPerHour (which has a default value of 0 in the ConfigFields). For consistency with the ConfigFields definitions, consider adding requestsPerHour to the DefaultConfig map with a value of 0, or alternatively remove the DefaultValue from the requestsPerHour field definition if 0 indicates "not set".

Suggested change
DefaultConfig: map[string]any{"requestsPerMinute": 60, "burstSize": 10},
DefaultConfig: map[string]any{"requestsPerMinute": 60, "requestsPerHour": 0, "burstSize": 10},

Copilot uses AI. Check for mistakes.
@intel352
Copy link
Contributor Author

@copilot open a new pull request to apply changes based on the comments in this thread

Copy link
Contributor

Copilot AI commented Feb 23, 2026

@intel352 I've opened a new pull request, #112, to work on those changes. Once the pull request is ready, I'll request review from you.

…tency (#112)

* Initial plan

* fix: clarify requestsPerHour precedence and add to DefaultConfig in rate limit schema

Co-authored-by: intel352 <77607+intel352@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: intel352 <77607+intel352@users.noreply.github.com>
@intel352 intel352 merged commit 32f4c4d into main Feb 23, 2026
12 of 13 checks passed
@intel352 intel352 deleted the fix/api-gateway-rate-limiter-isolation branch February 23, 2026 06:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Global rate limiter state in module/api_gateway.go breaks plugin isolation

3 participants