Membrane configuration generator
Membrane is configured declaratively. The modern format is apis.yaml; a legacy
Spring proxies.xml format also exists. This skill produces correct, idiomatic
config by doing what a careful engineer does: copy the shape from a real working
example, confirm exact attribute names against the schema, and validate before
handing it over.
Two sources of truth, and they play different roles:
distribution/tutorials/**/*.yaml — idiom and style. Heavily commented,
curated, and the best examples in the repo. Treat these as gold.
distribution/examples/** adds more (including proxies.xml).
membrane.schema.json — what's actually allowed: every element, its exact
attribute names, enum values, and nesting. The model's main failure mode is
inventing plausible-but-wrong attribute names; the schema is how you avoid that.
Don't generate config from memory alone. The element set is large (~255
elements) and evolves; ground every snippet.
Workflow
Clarify intent only if needed. What should the gateway do? Default to one
api on port 2000 forwarding to a target.url unless the request implies
otherwise. Don't over-ask — most requests map cleanly onto a known pattern.
Find a close example. Skim references/cheatsheet.md
for the matching pattern, then look at the real tutorial for the live version:
grep -rl "rateLimiter:" distribution/tutorials distribution/examples
Reading the closest tutorial is the single highest-leverage step — it carries
ordering, nesting, and conventions you'd otherwise guess at.
Confirm exact attributes against the schema for every non-trivial element.
Guessing attribute names is the main way these configs go wrong:
python3 scripts/describe_element.py rateLimiter # attributes + enums + children
python3 scripts/describe_element.py --grep auth # discover element names
python3 scripts/describe_element.py --list # all element ids
For any expression, template, or built-in function, read
references/expressions.md before writing it. The
schema does not cover this, and it's the easiest place to be confidently
wrong: which engine evaluates a string (template is Groovy, setBody/if
default to SpEL), how you call a function (${user()} in SpEL vs.
${fn.user()} in a Groovy template), which variables are in scope, and the
full built-in catalog (user(), base64Encode, hasScope, env, …). These
bugs surface as a runtime 500, not a schema error — validate_config.py won't
catch them.
Write the config following the conventions below.
Validate (YAML only) — always, before presenting:
python3 scripts/validate_config.py path/to/apis.yaml
Fix every reported problem and re-run until it passes. If you only produced a
snippet, drop it into a minimal api: skeleton in a temp file and validate
that, so you're confident the snippet is structurally sound.
The scripts find the schema automatically: they prefer the locally built
core/target/classes/com/predic8/membrane/core/config/json/membrane.schema.json
and fall back to downloading the published v7.4.0.json (cached). They
bootstrap their own dependencies on first run, so just call them.
Conventions
Match the tutorials so generated config looks native:
- Schema header. Start full files with
# yaml-language-server: $schema=https://www.membrane-api.io/<version>.json — it
gives editors autocompletion and validation. Pin <version> to whatever the
tutorials currently use (grep one: grep -m1 schema= distribution/tutorials/*/*.yaml),
not a hardcoded number here — it tracks the current release and goes stale otherwise.
- One top-level key per document. Each YAML document has exactly one root key
(
api, global, configuration, soapProxy, sslProxy). Separate multiple
definitions with a --- line.
- Default port 2000 (8443 for TLS), matching the tutorials.
- Comment with purpose. Tutorials briefly say what a block does and, where
useful, a
curl line to try it. Add short comments in that spirit — explain
intent, don't narrate syntax.
- Snippet vs. full file. If the user asked for a snippet to drop into an
existing config, return just the relevant
flow/plugin block at the right
indentation. If they asked for an example or a runnable config, return a
complete file with the header and a target or return.
Output
Present the config in a fenced code block with yaml or xml as its info string. Below it, add a line or
two on what it does and, when it makes the example concrete, how to run and test
it (e.g. ./membrane.sh -c apis.yaml then a curl). State that you validated it
against the schema. If a requested feature isn't in the schema (the local build
can lag the newest features — e.g. some AI plugins), say so and point to the
tutorial you based it on instead of forcing it through validation.
XML (proxies.xml)
Only when the user explicitly asks for XML / proxies.xml / the Spring format.
Same elements, camelCase tags, attributes instead of nested keys, wrapped in
<spring:beans>...<router>. Ground it in a real
distribution/examples/**/proxies.xml; see the XML section of
references/cheatsheet.md. validate_config.py does
not check XML, so lean harder on matching an existing example.
1---2name: membrane-config3description: Generate a Membrane API Gateway configuration example or snippet — an apis.yaml (default) or, when explicitly asked, a legacy proxies.xml. Use this whenever the user wants a config, example, or snippet for Membrane: routing a port to a backend, a flow with plugins (setHeader, rateLimiter, basicAuthentication, openapi, choose/if, static/return, template, validator, oauth2, llmGateway, ...), TLS termination, SOAP/REST transformation, or "how do I configure X in Membrane". Also trigger for phrases like "membrane config", "apis.yaml example", "proxies.xml", "add a plugin to my gateway config", or "show me the YAML for ...". Grounds output in the project's real tutorials and verifies it against membrane.schema.json.4---56# Membrane configuration generator78Membrane is configured declaratively. The modern format is `apis.yaml`; a legacy9Spring `proxies.xml` format also exists. This skill produces correct, idiomatic10config by doing what a careful engineer does: copy the shape from a real working11example, confirm exact attribute names against the schema, and validate before12handing it over.1314Two sources of truth, and they play different roles:15- **`distribution/tutorials/**/*.yaml`** — idiom and style. Heavily commented,16 curated, and the best examples in the repo. Treat these as gold.17 `distribution/examples/**` adds more (including `proxies.xml`).18- **`membrane.schema.json`** — what's actually allowed: every element, its exact19 attribute names, enum values, and nesting. The model's main failure mode is20 inventing plausible-but-wrong attribute names; the schema is how you avoid that.2122Don't generate config from memory alone. The element set is large (~25523elements) and evolves; ground every snippet.2425## Workflow26271. **Clarify intent only if needed.** What should the gateway do? Default to one28 `api` on port `2000` forwarding to a `target.url` unless the request implies29 otherwise. Don't over-ask — most requests map cleanly onto a known pattern.30312. **Find a close example.** Skim [references/cheatsheet.md](references/cheatsheet.md)32 for the matching pattern, then look at the real tutorial for the live version:33 ```bash34 grep -rl "rateLimiter:" distribution/tutorials distribution/examples35 ```36 Reading the closest tutorial is the single highest-leverage step — it carries37 ordering, nesting, and conventions you'd otherwise guess at.38393. **Confirm exact attributes against the schema** for every non-trivial element.40 Guessing attribute names is the main way these configs go wrong:41 ```bash42 python3 scripts/describe_element.py rateLimiter # attributes + enums + children43 python3 scripts/describe_element.py --grep auth # discover element names44 python3 scripts/describe_element.py --list # all element ids45 ```46474. **For any expression, template, or built-in function**, read48 [references/expressions.md](references/expressions.md) before writing it. The49 schema does **not** cover this, and it's the easiest place to be confidently50 wrong: which engine evaluates a string (`template` is Groovy, `setBody`/`if`51 default to SpEL), how you call a function (`${user()}` in SpEL vs.52 `${fn.user()}` in a Groovy template), which variables are in scope, and the53 full built-in catalog (`user()`, `base64Encode`, `hasScope`, `env`, …). These54 bugs surface as a runtime 500, not a schema error — `validate_config.py` won't55 catch them.56575. **Write the config** following the conventions below.58596. **Validate** (YAML only) — always, before presenting:60 ```bash61 python3 scripts/validate_config.py path/to/apis.yaml62 ```63 Fix every reported problem and re-run until it passes. If you only produced a64 snippet, drop it into a minimal `api:` skeleton in a temp file and validate65 that, so you're confident the snippet is structurally sound.6667The scripts find the schema automatically: they prefer the locally built68`core/target/classes/com/predic8/membrane/core/config/json/membrane.schema.json`69and fall back to downloading the published `v7.4.0.json` (cached). They70bootstrap their own dependencies on first run, so just call them.7172## Conventions7374Match the tutorials so generated config looks native:7576- **Schema header.** Start full files with77 `# yaml-language-server: $schema=https://www.membrane-api.io/<version>.json` — it78 gives editors autocompletion and validation. Pin `<version>` to whatever the79 tutorials currently use (grep one: `grep -m1 schema= distribution/tutorials/*/*.yaml`),80 not a hardcoded number here — it tracks the current release and goes stale otherwise.81- **One top-level key per document.** Each YAML document has exactly one root key82 (`api`, `global`, `configuration`, `soapProxy`, `sslProxy`). Separate multiple83 definitions with a `---` line.84- **Default port 2000** (8443 for TLS), matching the tutorials.85- **Comment with purpose.** Tutorials briefly say what a block does and, where86 useful, a `curl` line to try it. Add short comments in that spirit — explain87 intent, don't narrate syntax.88- **Snippet vs. full file.** If the user asked for a snippet to drop into an89 existing config, return just the relevant `flow`/plugin block at the right90 indentation. If they asked for an example or a runnable config, return a91 complete file with the header and a `target` or `return`.9293## Output9495Present the config in a fenced code block with `yaml` or `xml` as its info string. Below it, add a line or96two on what it does and, when it makes the example concrete, how to run and test97it (e.g. `./membrane.sh -c apis.yaml` then a `curl`). State that you validated it98against the schema. If a requested feature isn't in the schema (the local build99can lag the newest features — e.g. some AI plugins), say so and point to the100tutorial you based it on instead of forcing it through validation.101102## XML (proxies.xml)103104Only when the user explicitly asks for XML / proxies.xml / the Spring format.105Same elements, camelCase tags, attributes instead of nested keys, wrapped in106`<spring:beans>...<router>`. Ground it in a real107`distribution/examples/**/proxies.xml`; see the XML section of108[references/cheatsheet.md](references/cheatsheet.md). `validate_config.py` does109not check XML, so lean harder on matching an existing example.