Skip to content

feat: add protoc-gen-krakend API gateway config generator#123

Open
SebastienMelki wants to merge 57 commits intomainfrom
gsd/phase-12-annotations-and-core-endpoint-generation
Open

feat: add protoc-gen-krakend API gateway config generator#123
SebastienMelki wants to merge 57 commits intomainfrom
gsd/phase-12-annotations-and-core-endpoint-generation

Conversation

@SebastienMelki
Copy link
Owner

Summary

Adds protoc-gen-krakend — a new protoc plugin that generates KrakenD API gateway endpoint configurations directly from protobuf service definitions.

Why this matters

The problem: KrakenD configuration is handwritten JSON that must stay perfectly in sync with your backend API definitions. Every time you add an endpoint, change a path parameter, add a required header, or introduce a query filter, you must manually update the gateway config. This creates:

  • Configuration drift — the gateway config diverges from the actual API, causing silent request failures at the gateway layer (blocked headers, missing query params, wrong paths)
  • Duplicated source of truth — HTTP routing, header requirements, and query parameters are already defined in proto annotations (sebuf.http.config, sebuf.http.service_headers, sebuf.http.query), but must be re-specified in KrakenD JSON
  • KrakenD's zero-trust model amplifies this — by default KrakenD forwards nothing (no headers, no query strings). Every parameter must be explicitly allowlisted in input_headers and input_query_strings. Forgetting one breaks authentication, pagination, or filtering silently

The solution: protoc-gen-krakend reads the proto annotations you've already written and generates correct, minimal KrakenD endpoint fragments automatically:

  • sebuf.http.config → endpoint path and HTTP method
  • sebuf.http.service_headers + method_headersinput_headers (auto-derived, never wildcards)
  • sebuf.http.queryinput_query_strings (auto-derived from request message fields)
  • sebuf.krakend.gateway_config → backend host and default timeout
  • sebuf.krakend.endpoint_config → per-RPC overrides

The benefit: Proto definitions remain the single source of truth. Add an endpoint or header annotation once, regenerate, and the gateway config updates automatically. Route conflicts (duplicate paths, static vs parameterized collisions) are caught at generation time, not at gateway startup.

What's included (Phase 12 — in progress)

  • Plan 01 — Proto annotation package (sebuf.krakend), plugin scaffold, KrakenD JSON types
  • Plan 02 — Core endpoint/backend generation with host and timeout config
  • Plan 03 — Auto-derived header and query string forwarding from existing sebuf.http annotations
  • Plan 04 — Route validation (duplicate endpoints, static vs param conflicts) and golden file test suite

What's coming next (Phases 13-14)

  • Phase 13: Gateway features — rate limiting, JWT auth, circuit breaker, caching, concurrent calls
  • Phase 14: Documentation — example proto and Flexible Config integration guide

Test plan

  • make build produces bin/protoc-gen-krakend
  • go vet ./cmd/protoc-gen-krakend/... ./internal/krakendgen/... passes
  • make lint-fix passes
  • Golden file tests cover endpoint routing, timeouts, host config, forwarding, and validation errors (Plan 04)
  • ./scripts/run_tests.sh --fast — full project test suite passes with no regressions

🤖 Generated with Claude Code

SebastienMelki and others added 17 commits February 25, 2026 13:42
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add proto/sebuf/krakend/krakend.proto with GatewayConfig (ext 51001) and EndpointConfig (ext 51002)
- Generate krakend/krakend.pb.go with E_GatewayConfig and E_EndpointConfig extensions
- Update Makefile proto target to include krakend proto generation

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add cmd/protoc-gen-krakend/main.go following openapiv3 entry point pattern
- Add internal/krakendgen/types.go with Endpoint and Backend structs
- Plugin reads CodeGeneratorRequest, iterates services, outputs {Service}.krakend.json
- JSON types use omitempty for timeout, input_headers, input_query_strings

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Create 12-01-SUMMARY.md with execution results
- Update STATE.md with position, metrics, and decisions
- Update ROADMAP.md with plan progress
- Mark ANNO-01, ANNO-02, ANNO-03 requirements complete

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Read sebuf.krakend.gateway_config for service-level host/timeout
- Read sebuf.krakend.endpoint_config for method-level overrides
- Read sebuf.http.config/service_config for routing (path, method, base_path)
- Method-level endpoint_config overrides service-level gateway_config
- Missing gateway_config fails with clear error message
- Timeout omitted when not annotated at any level
- Output encoding always "json" on every endpoint

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Call krakendgen.GenerateService for each service in proto files
- Marshal endpoints to pretty-printed JSON with 2-space indent
- Write per-service files with trailing newline
- Propagate generator errors to protoc via plugin.Error
- Empty services (no HTTP RPCs) produce [] not null
- Fix: only require gateway_config when service has HTTP-annotated RPCs

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add deriveInputHeaders helper that extracts header names via CombineHeaders
- Integrate into GenerateService loop to populate InputHeaders on each endpoint
- Returns nil when no headers annotated (omitempty omits from JSON)
- Output sorted for deterministic golden file comparison

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add deriveInputQueryStrings helper that extracts param names via GetQueryParams
- Integrate into GenerateService loop to populate InputQueryStrings on each endpoint
- Returns nil when no query annotations exist (omitempty omits from JSON)
- Output sorted for deterministic golden file comparison
- Verified end-to-end: headers, query strings, and omission all work correctly

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@github-actions
Copy link

github-actions bot commented Feb 25, 2026

🔍 CI Pipeline Status

Lint: success
Test: success
Coverage: success
Build: success
Integration: success


📊 Coverage Report: Available in checks above
🔗 Artifacts: Test results and coverage reports uploaded

SebastienMelki and others added 12 commits February 25, 2026 15:28
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Detect duplicate (path, method) tuples at generation time
- Detect static vs parameterized segment conflicts via path trie
- Error messages include service name, endpoint indices, and conflicting paths
- Validation runs after endpoint generation, before returning from GenerateService

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…r KrakenD

- 9 test protos covering: core routing, timeouts, host config, header
  forwarding, query forwarding, combined forwarding, and 3 error cases
- 7 golden files for byte-for-byte regression detection
- TestKrakenDGoldenFiles validates successful generation with UPDATE_GOLDEN support
- TestKrakenDValidationErrors validates duplicate routes, static/param conflicts,
  and missing gateway_config produce clear error messages
- Full project test suite passes (10/10 packages)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ages, namespace constants, extend structs

- Wrap .krakend.json output in full KrakenD config object with $schema and version 3
- Add proto messages for rate limiting, JWT, circuit breaker, and cache
- Create namespace constants file with unit tests for all 5 KrakenD namespaces
- Add ExtraConfig and ConcurrentCalls fields to Endpoint and Backend structs
- Update all 7 existing golden files to new wrapped format

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…rage

- Add resolve/build functions for endpoint and backend rate limiting
- Integrate extra_config generation into GenerateService pipeline
- Create rate_limit_service.proto test case with service defaults and method overrides
- Add RateLimitService golden file covering qos/ratelimit/router and qos/ratelimit/proxy
- Method-level rate limit fully overrides service-level (no field merge)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…gation

- Add buildAuthValidatorConfig for auth/validator namespace config
- Add buildPropagateClaims for array-of-arrays claim format
- Add getJWTPropagatedHeaderNames for input_headers augmentation
- Update buildEndpointExtraConfig to include JWT from service-level config
- Auto-add propagated claim headers to input_headers with dedup and sort

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Create jwt_auth_service.proto exercising full JWT config
- Generate JWTAuthService.krakend.json golden file
- Verify auth/validator namespace with propagate_claims array-of-arrays
- Verify propagated claim headers auto-added to input_headers
- Add test case to TestKrakenDGoldenFiles (8 golden + 3 validation = 11 total)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
SebastienMelki and others added 19 commits February 25, 2026 16:31
…ation

- Add resolveCircuitBreaker, validateCircuitBreaker, buildCircuitBreakerConfig
- Add resolveCache, validateCache, buildHTTPCacheConfig
- Add resolveConcurrentCalls as top-level endpoint field (not extra_config)
- Integrate validation in GenerateService (fail-fast on invalid config)
- Wire circuit breaker and cache into buildBackendExtraConfig

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…cuit breaker, cache, concurrent calls

- Add circuit_breaker_service.proto with service/method-level override
- Add cache_concurrent_service.proto with caching and concurrent calls
- Add full_gateway_service.proto combining ALL gateway features
- Add invalid_circuit_breaker.proto and invalid_cache.proto for validation errors
- Generate golden files for all three new services
- Add validation error test cases for circuit breaker and cache

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…plan

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…m, fix cache oneOf bug

- Add RateLimitStrategy and JWTAlgorithm enums to krakend.proto
- Change strategy field from string to RateLimitStrategy enum
- Change alg field from string to JWTAlgorithm enum
- Add rateLimitStrategyToString and jwtAlgorithmToString mapping functions
- Add cache validation rejecting shared combined with max_items/max_size
- Update test protos to use enum values instead of string literals
- Fix CacheConcurrentService test proto removing shared+max_items conflict

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…files

- Add TestKrakenDSchemaValidation running krakend check -lc on all 12 golden files
- Test skips gracefully when krakend CLI is not installed
- Validates every generated config against KrakenD's official JSON schema

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…akenD links

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add protoc-gen-krakend as sixth generator in generators table
- Add KrakenD API Gateway section with proto annotations and JSON output examples
- Add krakend install command to Quick setup
- Add KrakenD Gateway Example link to Next steps
- Add KrakenD to Built on Great Tools section

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add protoc-gen-krakend as sixth plugin in project overview
- Document krakendgen architecture, annotations, and generated output
- Add KrakenD gateway annotations section with proto examples
- Add extension numbers 51001/51002 to registry table
- Document RateLimitStrategy and JWTAlgorithm enum types
- Add KrakenD testing commands (golden files, schema validation)
- Update project structure with krakend entries

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…file

- UserService proto: JWT auth (RS256), IP rate limiting, backend rate limiting, headers, query params
- ProductService proto: circuit breaker, caching (shared/sized), concurrent calls, header rate limiting
- Method-level overrides: UpdateUser (header strategy), GetProduct (sized cache), CreateProduct (aggressive CB)
- Makefile with generate/partials/validate/compose/clean targets using protoc directly

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Flexible Config template (krakend.tmpl) composes per-service endpoint partials
- Settings file lists services for template range loops
- README covers all KrakenD annotations with inline references to proto files
- Flexible Config integration guide: generate -> partials -> compose workflow
- Feature distribution table and step-by-step guide for adding new services
- .gitignore for generated artifacts (generated/, gateway/partials/, buf.lock)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- SUMMARY.md with task commits, deviations, and self-check
- STATE.md updated: all phase 14 plans complete, progress 100%
- ROADMAP.md updated: phase 14 marked complete (3/3 plans)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Generation now fails with a clear error when strategy is HEADER or PARAM
but no key is specified. IP strategy does not require a key.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@codecov
Copy link

codecov bot commented Feb 25, 2026

Codecov Report

❌ Patch coverage is 1.10132% with 449 lines in your changes missing coverage. Please review.
✅ Project coverage is 2.48%. Comparing base (871b387) to head (91d4e0f).

Files with missing lines Patch % Lines
internal/krakendgen/generator.go 0.00% 355 Missing ⚠️
internal/krakendgen/validation.go 0.00% 94 Missing ⚠️
Additional details and impacted files
@@           Coverage Diff            @@
##            main    #123      +/-   ##
========================================
- Coverage   2.64%   2.48%   -0.16%     
========================================
  Files         45      47       +2     
  Lines       7082    7531     +449     
========================================
  Hits         187     187              
- Misses      6892    7341     +449     
  Partials       3       3              
Flag Coverage Δ
unittests 2.48% <1.10%> (-0.16%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

SebastienMelki and others added 4 commits February 26, 2026 01:14
…-service files

The generator now merges all service endpoints into one ready-to-use
krakend.json, matching the DX of every other sebuf plugin (one protoc
invocation = usable output). Cross-service route conflict validation
is now performed at generation time.

The krakend-gateway example is simplified: removed the Flexible Config
partials/template workflow (jq, krakend.tmpl, settings/) and added a
runnable Go backend with Docker Compose so the gateway demo works
end-to-end.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add exhaustive switch cases for UNSPECIFIED enum values
- Extract helpers to reduce cognitive complexity in GenerateService and checkSegmentConflicts
- Eliminate err shadow declarations throughout generator.go
- Fix magic number (use const for KrakenD schema version)
- Suppress gosec G115 for guaranteed ASCII byte conversions
- Suppress gochecknoglobals for package-level namespace registry

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Auto-fix from golangci-lint.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@SebastienMelki
Copy link
Owner Author

How to use protoc-gen-krakend

Hey! Here's a detailed walkthrough of what this plugin does and how to integrate it into our workflow.

The problem it solves

KrakenD operates on a zero-trust model — by default it forwards nothing to backends. No headers, no query strings, no parameters. Every single one must be explicitly allowlisted in input_headers and input_query_strings in the KrakenD JSON config. On top of that, every endpoint path, HTTP method, timeout, rate limit config, JWT config, etc. must also be manually specified.

This means today we maintain two sources of truth: the proto definitions (which define the actual API) and the KrakenD JSON config (which must mirror it exactly). When someone adds a header annotation, a query parameter, or a new endpoint to a proto, they also have to remember to update the KrakenD config. If they forget, the gateway silently drops headers or blocks requests — no build error, no warning, just broken auth or missing pagination at runtime.

What protoc-gen-krakend does

It reads the annotations we've already written in our proto files and generates correct KrakenD JSON automatically:

Proto annotation KrakenD output
sebuf.http.config (path + method) endpoint, method, url_pattern
sebuf.http.service_headers + method_headers input_headers (auto-derived, no wildcards)
sebuf.http.query on request fields input_query_strings (auto-derived)
sebuf.krakend.gateway_config (service-level) host, timeout, rate limit, JWT, circuit breaker, cache, concurrent calls
sebuf.krakend.endpoint_config (method-level) Per-RPC overrides for any of the above

The proto becomes the single source of truth. Add an endpoint or header annotation once, run buf generate, and the gateway config updates automatically. Route conflicts (duplicate paths, static vs parameterized collisions like /users/me vs /users/{id}) are caught at generation time, not at gateway startup.

How to use it

Step 1: Add krakend annotations to your proto

At the service level, set defaults for all endpoints:

import "sebuf/krakend/krakend.proto";

service UserService {
  option (sebuf.krakend.gateway_config) = {
    host: ["http://users-backend:8080"]
    timeout: "3s"
    rate_limit: { max_rate: 100, client_max_rate: 20, strategy: RATE_LIMIT_STRATEGY_IP }
    jwt: {
      alg: JWT_ALGORITHM_RS256
      jwk_url: "https://auth.example.com/.well-known/jwks.json"
      audience: ["https://api.example.com"]
      issuer: "https://auth.example.com/"
      cache: true
      propagate_claims: [
        {claim: "sub", header: "X-User"},
        {claim: "org_id", header: "X-Org-ID"}
      ]
    }
    circuit_breaker: { interval: 60, timeout: 10, max_errors: 3 }
    backend_rate_limit: { max_rate: 80, capacity: 100 }
  };
}

Optionally override per-RPC:

rpc UpdateUser(UpdateUserRequest) returns (User) {
  option (sebuf.http.config) = { path: "/users/{id}", method: HTTP_METHOD_PUT };
  option (sebuf.krakend.endpoint_config) = {
    rate_limit: {
      max_rate: 50
      client_max_rate: 10
      strategy: RATE_LIMIT_STRATEGY_HEADER
      key: "X-API-Key"  // Per-API-key throttling for writes
    }
  };
}

Step 2: Add plugin to buf.gen.yaml

plugins:
  - local: protoc-gen-krakend
    out: gateway

Step 3: Generate

buf generate
# Output: gateway/krakend.json — a complete, valid KrakenD config

That's it. The output is a standalone krakend.json with $schema, version: 3, and all endpoints from all services combined. You can point KrakenD directly at it, or use it as a partial in KrakenD Flexible Config to compose with other settings.

What gets auto-derived (the best part)

You don't need to manually list forwarded headers or query params. The generator reads existing sebuf.http annotations:

  • Headers: service_headers required header names + method_headers required header names + JWT propagate_claims mapped headers → all merged, deduped, sorted → input_headers
  • Query strings: Fields with (sebuf.http.query) annotation on the request message → input_query_strings

Example: if ListUsersRequest has fields page, per_page, status annotated with (sebuf.http.query), the generated endpoint automatically includes:

"input_query_strings": ["page", "per_page", "status"]

No manual sync needed.

Features supported

Feature Where configured KrakenD namespace
Rate limiting (endpoint) rate_limit qos/ratelimit/router
Rate limiting (backend) backend_rate_limit qos/ratelimit/proxy
JWT authentication jwt (service-level only) auth/validator
JWT claim propagation jwt.propagate_claims auto-added to input_headers
Circuit breaker circuit_breaker qos/circuit-breaker
HTTP caching (shared) cache: { shared: true } qos/http-cache
HTTP caching (sized) cache: { max_items, max_size } qos/http-cache
Concurrent calls concurrent_calls: N top-level endpoint field
Timeout timeout: "3s" top-level endpoint field
Multi-host load balancing host: ["h1", "h2"] backend host array

All features can be set at service level (gateway_config) and overridden per-RPC (endpoint_config), except JWT which is service-level only (auth should be consistent across all endpoints in a service).

Generation-time validation

The plugin catches errors at build time instead of gateway startup:

  • Duplicate routes: Two RPCs resolving to the same METHOD /path → generation error
  • Static vs parameterized conflicts: /users/me and /users/{id} on the same method → generation error
  • Invalid cache config: shared: true with max_items/max_size (KrakenD's oneOf constraint) → generation error
  • Missing rate limit key: RATE_LIMIT_STRATEGY_HEADER without specifying key → generation error

Running the example

There's a full working example in examples/krakend-gateway/ with two services (UserService + ProductService), Docker Compose, and a real KrakenD gateway:

# From repo root
make build
cd examples/krakend-gateway
make demo   # generates config, builds backend, starts KrakenD + backend
make test   # curls the gateway endpoints
make docker-down

Documentation

TL;DR

Write your API in proto with sebuf annotations → run buf generate → get a validated, production-ready krakend.json with JWT, rate limiting, circuit breakers, caching, and all headers/query params correctly forwarded. No manual KrakenD JSON maintenance. The proto is the single source of truth.

@SebastienMelki SebastienMelki marked this pull request as ready for review February 26, 2026 13:18
SebastienMelki and others added 4 commits February 26, 2026 15:20
- Replace byte() casts with unicode.ToLower in camelToSnake to avoid
  gosec G115 vs nolintlint conflict across golangci-lint versions
- Run buf format on proto files (trailing whitespace, missing EOF newlines,
  comment alignment)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The krakend check -lc flag fetches the JSON schema from krakend.io,
which can timeout in CI or on slow networks. Switch to syntax-only
validation (-c) by default. Full online schema lint can be enabled
with KRAKEND_LINT=1 when needed.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Drop protoc 3.19.0 and 3.20.0 which arduino/setup-protoc@v3 can no
  longer resolve from GitHub releases ("unable to get latest version")
- Use protoc 25.1 and 28.3 (current stable versions)
- Add fail-fast: false so all matrix versions run independently

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
protoc-gen-go@latest now requires Go >= 1.23. The proto compatibility
job was using Go 1.21 which is too old. Upgrade to 1.24 to match the
main CI workflow.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
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.

1 participant