HTTP Plugins

Octelium already supports native ways to manipulate HTTP request/response headers, validate request JSON body and send direct responses back to the downstreams (read more here). However, in many real-world use cases, such native methods might be not flexible enough.

Octelium provides additional, more advanced "plugins" to manipulate HTTP requests and responses in arbitrarily complex ways that might include calling external SaaS HTTP-based APIs and databases, manipulating JSON body content of requests and responses, etc.... Such plugins can be especially useful for many use cases that can be applied to ZTNA/BeyondCorp, API gateways, AI gateways, MCP gateways, etc.... including:

  • Request/response sanitization and manipulation for headers as well as body content, including JSON, which can be used for API/AI gateways.
  • Custom identity-based caching, including semantic caching for AI gateways.
  • DLP for ZTNA/BeyondCorp.
  • Implementing AI guardrails, including calling SaaS APIs to verify prompts.
  • Custom identity-based rate limiting.

Currently 2 plugin types are supported: Lua scripts and Envoy's ext_proc filter. Octelium enables you to stack one or more plugins from the same or different types and invoke them at certain phases of the request, as well as to conditionally invoke them via policy-as-code rules on a per-request basis. Plugins are invoked in the same order they are defined.

Lua

Lua plugin invokes a Lua script that includes two functions: onRequest that is invoked upon receiving the request before proxying it to the upstream, and onResponse that is invoked upon receiving the response from the upstream. Here is a detailed example:

1
kind: Service
2
metadata:
3
name: svc1
4
spec:
5
mode: HTTP
6
config:
7
upstream:
8
url: https://api.example.com
9
http:
10
plugins:
11
- name: my-lua-script
12
lua:
13
inline: |
14
function onRequest(ctx)
15
octelium.req.setRequestHeader("X-User-Uid", ctx.user.metadata.uid)
16
octelium.req.deleteRequestHeader("X-Delete")
17
octelium.req.setQueryParam("user", ctx.user.metadata.name)
18
octelium.req.deleteQueryParam("param1")
19
local body = json.decode(octelium.req.getRequestBody())
20
if strings.contains(strings.toLower(strings.trim(base64.decode(body.strField))), "privileged") then
21
local res = {
22
error = "You are not allowed"
23
}
24
octelium.req.setResponseBody(json.encode(res))
25
octelium.req.exit(403)
26
end
27
28
if strings.hasPrefix(ctx.request.http.path, "/users") then
29
octelium.req.setPath("/users/"..ctx.user.metadata.uid)
30
end
31
32
if strings.hasSuffix(ctx.request.http.path, ".php") then
33
octelium.req.setPath("/users/"..ctx.user.metadata.uid)
34
end
35
36
if strings.len(body.someStrField) > 1000 then
37
local c = http.client()
38
c:setBaseURL("http://my-api.default.svc")
39
local req = c:request()
40
req:setBody(json.encode(ctx.user))
41
req:setHeader("X-User-Uid", ctx.user.metadata.uid)
42
local resp, err = req:post("/v1/check-user")
43
if err then
44
octelium.exit(500)
45
end
46
47
if resp:code() == 200 then
48
local apiResp = json.decode(resp:body())
49
if not apiResp.userIsAllowed then
50
octelium.exit(403)
51
end
52
end
53
end
54
end
55
56
function onResponse(ctx)
57
octelium.req.setResponseHeader("X-Session-Uid", ctx.session.metadata.uid)
58
octelium.req.deleteResponseHeader("X-Delete")
59
local resp = octelium.req.getResponseBody()
60
resp.email = ctx.user.spec.email
61
octelium.req.setResponseBody(json.encode(resp))
62
octelium.req.setStatusCode(209)
63
end

Note that you do not have to define both onRequest and onResponse in every Lua script. If any of these functions does not exist, then it is silently skipped.

Envoy Ext Proc

Octelium also supports Envoy's ext_proc. In this plugin type, Octelium's identity-aware proxy, Vigil, behaves like an Envoy instance and acts as an ext_proc gRPC client that sends ProcessingRequest to your ext_proc gRPC compliant server and waits for ProcessingResponse on every request. Here is an example:

1
kind: Service
2
metadata:
3
name: svc1
4
spec:
5
mode: HTTP
6
config:
7
upstream:
8
url: https://api.example.com
9
http:
10
plugins:
11
- name: my-ext-proc-svc
12
extProc:
13
address: ext-proc.default.svc:8080

Phase

By default, plugins are invoked after the authentication and authorization processes are done. However, you might choose to invoke a plugin before authentication/authorization by explicity setting a phase value for that plugin. Currently the values available are: POST_AUTH which is the default behavior when not explicitly set, and PRE_AUTH to force a pre-auth invocation. PRE_AUTH plugins might be useful to do pre-authentication manipulation or even completely dropping the requests before proceeding further to the authentication/authorization process.

Note that PRE_AUTH plugins must be in the default Service configuration. In other words, you can not have a PRE_AUTH in a named dynamic config (read more here). Here is an example:

1
kind: Service
2
metadata:
3
name: svc1
4
spec:
5
mode: HTTP
6
config:
7
upstream:
8
url: https://api.example.com
9
http:
10
plugins:
11
- name: check-body-size
12
phase: PRE_AUTH
13
lua:
14
inline: |
15
function onRequest(ctx)
16
local body = octelium.req.getRequestBody()
17
if strings.len(body) > 10000 then
18
octelium.req.exit(400)
19
end
20
end

Rules

By default, a plugin in the chosen Service config is invoked on all requests. You can, however, change that behavior and set per-request conditions for the plugin invocation. Here is an example that only invokes the plugin for paths whose prefix equals to /users:

1
kind: Service
2
metadata:
3
name: svc1
4
spec:
5
mode: HTTP
6
config:
7
upstream:
8
url: https://api.example.com
9
http:
10
plugins:
11
- name: my-ext-proc-svc
12
rules:
13
- condition:
14
match: ctx.request.http.request.startsWith("/users")
15
effect: ENFORCE
16
- condition:
17
matchAny: true
18
effect: IGNORE
19
extProc:
20
address: ext-proc.default.svc:8080

Disabling a Plugin

You can disable plugin as follows:

1
kind: Service
2
metadata:
3
name: svc1
4
spec:
5
mode: HTTP
6
config:
7
upstream:
8
url: https://api.example.com
9
http:
10
plugins:
11
- name: my-plugin
12
isDisabled: true
13
lua:
14
inline: |
15
function onRequest(ctx)
16
end
© 2025 octelium.comOctelium Labs, LLCAll rights reserved
Octelium and Octelium logo are trademarks of Octelium Labs, LLC.
WireGuard is a registered trademark of Jason A. Donenfeld