0.5

Every published okengine release, newest first.

all series Β· 0.6 Β· 0.4

v0.5.1

πŸ’₯ Breaking Changes

  • Official plugin rename: securityHeaders() / "security-headers" β†’ headers() / "headers" (options type HeadersOptions; config table headers_config). Docs page was already /docs/plugins/headers.

♻️ Changed

  • Rebased the plugins export budget after passkey WebAuthn verify (13.21 kB β†’ 13.84 kB), and refreshed the published budgets snapshot.
  • site joins the version lockstep (bun run bump updates site/package.json with okengine + create-oke).
  • Site depends on @fumadocs/base-ui (^) directly β€” dropped the fumadocs-ui npm alias.

πŸ”’ Security

  • twoFactor TOTP verify uses constant-time comparison across the Β±1 step window (no plain === short-circuit).
  • passkey register / authenticate now require a real WebAuthn ceremony: clientDataJSON origin + challenge, authenticatorData rpId hash, and ECDSA P-256 signature against the stored SPKI public key. Presence-only authenticate is rejected; challenges are single-use. New option origins.
  • Exploit-proof HTTP audit tests for all seven Gate auth method plugins (rate limits, gate posture, single-use tokens, anonymous non-escalation, Channel delivery gap, TOTP compare, passkey signature/origin).

v0.5.0

✨ Added

  • oke({ gate }) β€” nested Gate bag (auth, policies, rateLimit, unguardedHttp). Builtin hybrid auth materializes /auth/* as real Bindings on the same ensureBoot β†’ doBoot posture path; .needs("auth") is satisfied without .plug(auth()). Schema customize + oke schema generate emit real columns; opt-in cookies, kv secondary, hooks, freshAge, auth rate presets, enumeration-safe credential errors. Docs Β· [Gate](/docs/elements/gate).
  • okengine/client/auth helpers (memorySession, AUTH_ERROR_CODES) and okengine/client-react useSession β€” still one client (createClient).
  • Gate auth method plugins (okengine/plugins): username, anonymous, magicLink, emailOtp, phoneNumber, twoFactor, passkey β€” each .needs("auth") and contributes HTTP Bindings under /auth/.... Docs Β· one page per method under [Plugins](/docs/plugins) (username, anonymous, magic-link, email-otp, phone-number, two-factor, passkey).
  • Docs Β· [Client](/docs/reference/client) β€” okengine/client reference (createClient forms, REST vs RPC, Bearer auth + gate denials, elements-from-the-client, envelope helpers, oke client add).
  • Homepage **Onboard AI** button (hero CTAs) copies a short bootstrap prompt pointing at the live /llms.txt index and the repo-root AGENTS.md β€” distinct from the per-page docs β€œCopy prompt” action.
  • Docs section landings at /docs/get-started, /docs/elements, /docs/plugins, /docs/console, /docs/reference, and /docs/ai β€” definition plus Cards routing for direct URLs (not listed in the sidebar).
  • Site 404 page β€” branded β€œNo Flow matched” composition with the unmatched path, a probing triggerβ†’Effects visual, and home / docs CTAs.
  • gate.scope(name) β€” sugar for gate.policy(name, ({ auth }) => auth.scopes.has(name)) (single source of truth for the scope string).
  • gate.public β€” explicit public sentinel for intentionally unauthenticated HTTP surfaces; name "public" is reserved.
  • GateBootError β€” every HTTP trigger must carry a gate or gate.public at boot (fail loud). Test-only opt-out: unguardedHttp: "allow" when env === "test" (createTestApp opts out for the harness).
  • Docs Β· [Gate](/docs/elements/gate), [fx](/docs/reference/fx), [Plugins](/docs/reference/plugins), [Client](/docs/reference/client), [Console Gates](/docs/console/gates) β€” auth posture, gate.scope / gate.public, fx.principal, .needs(), unguarded vs explicit public.
  • fx.principal β€” read-only originating identity for audit; propagates across fx.call without filling fx.auth (authorization stays fail-closed).
  • Auth password policy (assertPasswordPolicy), Bun.password cost knobs (memoryCost / timeCost with Bun argon2id floor), pluggable breach-check + createHibpBreachCheck (HIBP k-anonymity range API), and session idle / absolute / single-session-per-user options on auth().
  • Plugin .needs() resolution at boot (PluginNeedsError) for plugin names and element/driver ids.
  • Plugin element contributions: .vault(), .clock(), .signal(), .gate(), .channelTemplate() β€” merged into boot options.

♻️ Changed

  • Root oke({ auth }) / oke({ gates }) / oke({ unguardedHttp }) moved to oke({ gate: { auth, policies, unguardedHttp } }) (hard migrate). Console uses gate.auth with http: false (keeps /console/session/*).
  • Plugin .binding() contributes HTTP Bindings into the app router on .plug() (auth method plugins); SmartRouter build is deferred until first match so plugs can still add routes.
  • fx.metric recorded as investigated and **declined**: Runs wide events already cover per-invocation observability; no parallel counter/gauge API (see reports/2026-07-31-fx-metric-decision.md and the fx reference).
  • Console HTTP bindings auto-attach gate.public or console:operator for auth posture; Gates panel distinguishes unguarded vs explicit public.

πŸ”’ Security

  • HTTP empty gate chains no longer silently public at boot β€” declare a gate or gate.public. unguardedHttp: "allow" is honoured **only** when env === "test" (never a production-wide bypass).
  • autoBoot now defaults to true β€” the first fetch / execute runs full boot (gate posture, vault, capabilities, element pipeline). Pre-boot ungated execution requires an explicit oke({ autoBoot: false }) (unit-test escape hatch only). No shipping path had been setting autoBoot: true; CLI/Console already called .boot() explicitly, but bare oke({…}).fetch() was silently ungated.
  • Default password policy enforced on createOperator (minLength 12, letter + number); tests needing short passwords must pass skipPasswordPolicy: true explicitly.
  • POST /auth/sign-up/email maps password-policy / breach-check failures to AuthFailed (reason: "password_policy" | "password_breached") instead of an empty 204 from an unhandled throw. Weak passwords were never stored; the status alone looked like silent success.

πŸ› Fixed

  • gate.public is always seeded into the Gate runtime β€” attaching .gate(gate.public) on a trigger no longer denies with Unauthorized / unknown gate: public when the sentinel was not also listed in oke({ gates }).
  • Console .oke/console.sqlite sessions persist and hydrate last_active_at (idle TTL); older DBs gain the column on open.
  • Site next build no longer fetches Inter from Google Fonts at build time: Inter Variable is vendored under site/app/fonts/ (OFL) and loaded via next/font/local, so offline / network-restricted CI builds cannot fail with a fonts.gstatic.com 403.
  • Unhandled throws in HTTP Flows encode as 500 InternalError β€” never as empty 204 (which encodeSuccess(undefined) previously produced).
  • Docs Β· auth method plugin Quick Starts enable oke({ gate: { auth } }) before .plug(...) (.needs("auth")); Client notes built-in POST /auth/refresh; Gate docs cover session idle/absolute/singleSessionPerUser and createHibpBreachCheck; drop false Console rate-limit retune claims.
  • Homepage element lattice β€” compact phone layout, stacked chrome, and no spotlight/hover chase on coarse pointers (touch).