Third-party plugin documentation skill
This skill guides you through creating complete documentation for a new third-party Kong Gateway plugin on developer.konghq.com.
The output is a new directory under app/_kong_plugins/<plugin-slug>/ containing index.md, schema.json, and at least one examples/*.yaml file.
Use the Skyflow De-identify plugin (app/_kong_plugins/skyflow-ai-data-control/index.md) as the canonical worked example of a complex plugin, and the Noma Runtime Protection (app/_kong_plugins/noma-runtime-protection/index.md) and TrendAI API Security (app/_kong_plugins/trend-micro-kong-plugin-aps/index.md) plugins as examples of simpler ones. For a vendor shipping multiple sibling plugins from one LuaRock, use the Harness AI Security / Harness WAAP pair (app/_kong_plugins/harness-ai-security/index.md, app/_kong_plugins/harness-waap/index.md) as the model: separate pages with install steps written inline per page, not deduplicated into a shared include.
Read references/patterns.md before drafting anything. It contains the front matter schema, body section order, navtab patterns, style rules, and schema.json structure.
Step 1: Gather source material
Do not draft anything until this step is complete and confirmed. The interview is mandatory.
Collect the following. Where information is ambiguous, ask rather than assume.
Plugin identity
- Display name (shown to users)
- Plugin slug: the kebab-case
namefield from the plugin'sschema.luaorschema.json— this becomes the directory name and the value in thename:front matter field - LuaRock name and current version string, for example
my-plugin-1.2.0-1(needed for theluarocks makecommand) - Publisher identifier: kebab-case company name (for example
skyflow,noma,trend-micro)
URLs and metadata
- Support URL
- Source code URL (GitHub repo, if public)
- Tags (for example
security,ai,logging) - Search aliases (alternative names users might search for)
- Minimum supported gateway version, if known — omit entirely if unknown, never use an empty string
Products and deployment
- Products:
gatewayand/orai-gateway works_on:on-premand/orkonnect- If the plugin targets AI Gateway, ask whether it's been validated against AI Gateway 2.0. If not, see the Callouts section in patterns.md for the warning to add.
Schema
- The plugin's schema, as either a
schema.luafile (provide a path or URL) or aschema.jsonfile - If Lua: read the file, extract all
configfields with their types, defaults, and descriptions, and produce aschema.json(seereferences/patterns.mdfor the JSON Schema structure)
Installation
- How is the plugin distributed? Options: LuaRock only, bare
schema.lua/handler.luafiles only, a custom Docker image, or a combination. Not every plugin uses LuaRocks. - If distributed as a LuaRock: the LuaRock name and current version string (for example
my-plugin-1.2.0-1), and the path to the.rockspecfile relative to the repo root (for theluarocks makecommand). - Does it support Konnect Streamed plugin upload? If so, what environment variables must be set on the data plane nodes?
- Runtime dependencies beyond those already in the Kong/OpenResty runtime.
Examples
- At least one example use case: which config fields are set, and what behavior does it produce?
- If the plugin has distinct modes or postures (for example de-identify only vs. de-identify + re-identify), each distinct mode should become its own example file
Publisher check
Check whether the publisher slug already exists in
app/_data/plugin_publishers.ymlIf not, ask for the publisher's display name and add an entry:
publisher-slug: name: Publisher Display NameAlso add the vendor name (lowercase), in alphabetical order, to
.github/styles/base/Dictionary.txt, so Vale doesn't flag it as an unknown spelling.
Icon check
- Check whether the icon file exists at
app/assets/icons/plugins/<icon-filename>.svg - The icon filename in the front matter should be
<publisher-slug>.svgor<plugin-slug>.svg - If the file is not present, note it as a gap in the pre-draft confirmation and tell the user:
"The icon file
<filename>.svgdoes not exist inapp/assets/icons/plugins/. You'll need to obtain it from the vendor and add it before the plugin page will render correctly." - Do not block drafting the docs on this — just flag it clearly.
Multiple plugins from one vendor/rock
- Ask whether the plugins interact: mutually exclusive (attach only one per Route), complementary (attach together), or dependent (one requires the other already attached). This determines how strongly to cross-link them and whether a decision aid (e.g. a small flowchart) would help the reader pick the right one.
- Give each plugin its own
app/_kong_plugins/<slug>/directory and cross-link siblings (seerelated_resourcesguidance in patterns.md).
Step 2: Assess complexity
Use this classification to calibrate how much content to write.
Simple plugin (DataDome, Moesif, TrendAI pattern)
- Few flat config fields (1-5 API keys or settings)
- No multi-phase request/response lifecycle
- Installation is one or two commands
- One example file is sufficient
- No sequence diagram needed
Complex plugin (Noma, Skyflow, Impart, CrowdStrike pattern)
- Nested or many config fields
- Plugin acts in multiple Kong lifecycle phases (access, response, log)
- Request body is inspected and rewritten
- Installation has meaningful Konnect-specific steps
- Multiple example scenarios warranted
- Sequence diagram recommended; flowchart if topology is non-trivial
Step 3: Pre-draft confirmation
Before writing anything, present a checklist of what you collected. Flag any gaps explicitly. Wait for the user to confirm or fill in what is missing.
Example format:
Ready to draft. Here's what I have:
Plugin identity
- Display name: Skyflow De-identify
- Slug: skyflow-ai-data-control
- LuaRock: skyflow-ai-data-control-0.7.0-1
- Publisher: skyflow (exists in plugin_publishers.yml)
Schema
- Source: schema.lua at [URL] — will convert to schema.json
Installation
- Self-managed: luarocks make + KONG_PLUGINS env var
- Konnect: streamed plugin (schema.lua + handler.lua upload)
- Env vars: KONG_CUSTOM_PLUGIN_STREAMING_ENABLED=on, KONG_UNTRUSTED_LUA=lax, KONG_PLUGINS=bundled
Examples
- Example 1: de-identify only (reidentify.enabled = false)
- Example 2: de-identify + re-identify (default posture)
Gaps: none
Complexity: complex (multi-phase, nested config, Konnect streamed)
Proceed?
Step 4: Draft all files
Draft in this order:
1. schema.json
Read references/patterns.md for the full JSON Schema structure. Every config field from the plugin's schema becomes a property in config.properties. Use the standard scope fields (consumer, consumer_group, route, service, protocols) unchanged.
Place at: app/_kong_plugins/<slug>/schema.json
2. index.md
Read references/index-template.md for the annotated template. Replace all <!-- PLACEHOLDER --> markers with real content.
Key rules:
- Never use em dashes
{{site.base_gateway}}for "Kong Gateway" / "Kong"{{site.konnect_short_name}}for "Konnect"- One sentence per line in prose
- No empty
min_version— omit the field if unknown - Bold only UI labels, not prose emphasis
- Sentence-case headings
- Refer to another plugin in prose by its title-case display name, linked to its page (e.g. "Request Transformer"), not its code-style slug. A slug in code style is fine only when it's illustrating a literal config value, e.g. inside
KONG_PLUGINS=bundled,request-transformer - Avoid a "not X, Y" construction (a clause stating what something is not before saying what it is). Rephrase as a direct positive statement instead
Place at: app/_kong_plugins/<slug>/index.md
3. examples/*.yaml
Read references/example-template.md for the annotated template. One file per scenario.
Naming: <verb>-<what-it-does>.yaml, for example deidentify-only.yaml, enable-noma-runtime-protection.yaml.
Weight: 900 for the primary example, 901 for secondary, and so on.
Place at: app/_kong_plugins/<slug>/examples/<name>.yaml
4. reference.md stub
The platform renders the schema reference page automatically from schema.json. Create a minimal stub:
See [schema reference](./reference/)
Place at: app/_kong_plugins/<slug>/reference.md
Step 5: Review and iterate
Present the drafted files. Accept feedback and revise. Pay particular attention to:
- Config field descriptions: are they accurate to the actual schema?
- Example variable names: do they match what a user would actually set in their environment?
- Installation steps: are they complete enough for someone who has never installed a custom plugin?
- Internal contradictions: does a caveat or warning in one section hold up against claims made elsewhere on the page (or a sibling plugin's page)?