# Changelog 0.5

## v0.5.1 — 2026-08-01

### 💥 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 — 2026-08-01

### ✨ 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).
