Plugins

IP Allowlist

Official plugin — allow/deny rules by client IP at the edge of the pipeline, with the same typed Forbidden denial the gate element produces.

ipAllowlist() enforces IP rules at onAuth, before any gate policy or flow body runs. Internal admin surfaces, staging environments, and webhook endpoints stop unknown clients at the edge with the same typed Forbidden denial the gate element produces.

Quick start

src/app.ts
import { oke } from "okengine";
import { ipAllowlist } from "okengine/plugins";

export const app = oke({ name: "shop", env: "dev" }).plug(
  ipAllowlist({ allow: ["203.0.113.7", "2001:db8::42"] }),
);

A client whose IP is not on the list receives 403 with a typed denial:

{
  "data": null,
  "error": {
    "code": "Forbidden",
    "data": { "reason": "ip_not_allowed", "ip": "198.51.100.9" }
  }
}

Options

OptionTypeDefaultDoes
allowstring[]— (everyone passes)Exact IPs permitted — every other client is denied
denystring[]— (nobody blocked)Exact IPs blocked — checked first, so deny wins on overlap
headerstring"x-forwarded-for"Header carrying the client IP
trustedProxyDepthnumber1Trusted proxies that append XFF; client IP is that many from the right
.plug(ipAllowlist({ deny: ["198.51.100.9"], trustedProxyDepth: 1 }))

Standard reverse proxies append to X-Forwarded-For — left-side hops are attacker-controlled. The plugin trusts the hop trustedProxyDepth from the right (default 1 = last hop). Set this to your real proxy count; wrong depth bypasses the allowlist — topology-dependent, not drop-in.

Notes

BehaviorDetail
XFF parsingLast hop (depth 1) is the client; left-side spoofed entries are ignored
Missing headerDenied when allow is set (unknown is not allowed); permitted for deny-only rules
Deny winsAn IP in both lists is blocked
Non-HTTP triggersNo-op — there is no client IP outside HTTP

Runtime configuration

Block an abusive IP from the database and every instance picks it up on the next sync:

const rules = configSource({
  plugin: "ip-allowlist",
  code: { deny: [] },
  db: { store: db },
  kv: cache,
});
on(every("30s"), rules.sync());
export const app = oke({ name: "shop", env: "dev" }).plug(ipAllowlist(rules));

See Plugins → Runtime configuration for the full contract.

Next

On this page