Reference

Security

Host and Origin allow-lists, user vs operator planes, Console and MCP posture.

Every served request on the backend, Console, and app MCP passes Host / Origin checks before your Flow runs. Private bind addresses are not a substitute for that check.

Reach for this page when you put a public hostname in front of the app, open Console in production, or wire MCP tokens.

The one rule

Pass every public hostname in allowedHosts on createBunRuntime().serve (and the Console / MCP serve options). Loopback is always allowed; Origin: null is always rejected. Failures return 403.

Smallest Example

Serve with an explicit host allow-list

src/app.ts
import { createBunRuntime } from "okengine/http";
import { app } from "./app";

createBunRuntime().serve(app, {
  port: Number(process.env.PORT ?? 6530),
  hostname: "0.0.0.0",
  allowedHosts: ["app.example.com", ".example.com"],
});

.example.com allows a.example.com (Vite-style suffix). Always merged with localhost · 127.0.0.1 · ::1 and the listen hostname when it is not a wildcard bind.

Confirm a bad Host is refused

curl -i http://127.0.0.1:6530/health -H "Host: evil.example"

Expect 403 with body Forbidden: unexpected Host header.

Progressive Patterns

Edge terminates TLS and forwards to the app. The Host the app sees must be on the allow-list — usually the public name, not the container hostname.

createBunRuntime().serve(app, {
  hostname: "0.0.0.0",
  allowedHosts: ["app.example.com"],
});

Consequence: omitting the public name behind Caddy / nginx / Traefik looks like a random 403 to browsers that send the site's Host.

Host / Origin Rules

CheckBehavior
HostRequired; must match the effective allow-list
OriginWhen present, host must match; Origin: null → 403
DefaultsAlways includes loopback + listen hostname (unless 0.0.0.0 / ::)
ExtrasServeOptions.allowedHosts — never replaces the mandatory check

allowedHosts lives on serve options, not in oke.config.ts. Pass it wherever you call serve for 6530, Console 6533, and app MCP 6535.

Console Posture

ControlMeaning
Host / OriginSame allow-list logic as the backend
CSPdefault-src 'self'; frame-ancestors 'none'
CookiesSameSite=Strict
Claim codePrinted on oke dev TTY; oke console claim-code while setup is open
PIIStore / Call API mask classified fields unless revealPii: true (audited)

Production Console: set OKE_CONSOLE_SECRET and configure console.prod in Configuration.

Sessions and Audiences

AudienceSurface
oke-appApplication sessions
oke-consoleConsole operators
oke-mcpApp MCP

Short access JWT (default ~14m) + rotating refresh. Refresh-token reuse revokes the family.

Troubleshooting

Learn more

Next

On this page