# Changelog 0.3

## v0.3.6 — 2026-07-31

### ✨ Added

- `oke-ship` agent skill — after every implementation, append changelog notes under `## Unreleased` and sync site docs via `oke-docs`. Wired into `AGENTS.md` so sessions close work with changelog + docs.

- `libsql` and `pglite` Store SQL drivers as optional peers. libSQL adds native `F32_BLOB` / `vector_top_k` ANN; PGlite provides opt-in PostgreSQL + pgvector parity without changing the local SQLite default.

- Real pgvector ANN for PostgreSQL and PGlite: `vector(dims)` storage, HNSW with cosine operators, and similarity-ranked search through Drizzle's native vector API.

- Optional `description` on element declarations — `store.*`, `signal`, `channel.template`, `clock`, `gate.policy` / `gate.rate`, `field.*.describe(…)`, and `plugin.table` — extracted into the Manifest and preferred as Console titles when set (raw key fallback).

- `fx.all` / `fx.race` — structured concurrency on plain Promises: pass thunks, first failure or settle aborts sibling branches via an ambient `AbortSignal` (`fx.signal`). No generators or monadic wrapper.

- `fx.retry(fn, { retries, delay, backoff, jitter, when? })` — exponential backoff with full jitter. Prefer inside `fx.step` so durable replay skips completed work.

- `flow({ retry })` — coarse whole-`do` retry on the same journal session (rewinds the durable cursor between attempts).

### ♻️ Changed

- `bun run bump` promotes `## Unreleased` into `## v{next} — {today}` (and leaves a fresh empty Unreleased). Changelog notes for unfinished work no longer land on an already-shipped version section.

- SQL-backed indexes share the SQL facet's existing connection. PGlite remains an explicit dialect-parity choice; measured WASM startup and warm CRUD remain meaningfully slower than native SQLite.

### 🐛 Fixed

- `store.index()` now honors `drivers.store.index` at real boot and uses the same driver switch in the Console. Explicit `pgvector` or `libsql` configuration fails loudly when its SQL engine, extension, or optional peer is unavailable instead of silently using memory.

## v0.3.5 — 2026-07-31

### ♻️ Changed

- `bun run ci` is the single local pre-push gate (format, lint, typecheck, integration tests, budgets, gate, site). Removed the unused `ci:workflow` split.

- Tag-push CI no longer re-runs budgets (local-only); parallel jobs stay lint/fmt, typecheck, test, gate, site → publish.

- GitHub Actions runtimes upgraded off deprecated Node 20: `actions/checkout@v6`, `oven-sh/setup-bun@v2.2.0`, `actions/setup-node@v6`.

## v0.3.4 — 2026-07-30

### 🐛 Fixed

- Re-release of v0.3.3 so tag CI can publish: OpenBao bootstrap waits for Raft `/sys/health` active before policy writes, and the `plugins` export budget is rebased after the CORS / IP-allowlist hardening.

## v0.3.3 — 2026-07-30

### ✨ Added

- `ipAllowlist({ trustedProxyDepth })` — how many trusted proxies append to `X-Forwarded-For` (default `1`). Client IP is taken that many hops from the right, matching how reverse proxies append rather than overwrite.

### ♻️ Changed

- Site logo assets live under `site/public/logo/` (wordmarks + letter variants); README and favicon paths follow. The docs site cycles the O / K / E favicon letters.

- Rebased the `plugins` export budget after the CORS / IP-allowlist hardening (4.45 kB → 4.74 kB), and refreshed the published budgets snapshot.

### 🐛 Fixed

- `cors({ origin: "*", credentials: true })` now throws at construction (and again if runtime config introduces the pair) instead of reflecting the request origin — that rewrite would have granted any site credentialed access.

- `ipAllowlist` no longer trusts the leftmost XFF hop (spoofable when proxies append). Wrong or non-positive `trustedProxyDepth` fails loud.

- OpenBao bootstrap waits for `/sys/health` active (Raft leader writable) after unseal before mounts/policies — fixes flaky restart CI when single-node Raft still reports `readonly storage`.

## v0.3.2 — 2026-07-30

### ♻️ Changed

- Rebased the `vault-openbao` export budget after a genuine size increase (608 B → 1.06 kB), and refreshed the published budgets snapshot.

### 🐛 Fixed

- Docs skill forbidden-token lists no longer trip the stack-mode and removed-driver `git grep` gates (tokens are split the same way the gate tests avoid matching themselves).

- Restored the missing `bun run gate` script so CI’s gate job runs again (staleness, removed-driver, error-registry, and upgrade codemod checks).

- Renamed and scrubbed the removed-driver gate file so it does not match its own forbidden id under case-insensitive search.

## v0.3.1 — 2026-07-30

### ♻️ Changed

- Docs origin cut over from `okengine.vercel.app` to `[oke.omqkhafi.dev](https://oke.omqkhafi.dev)`. Error links, README, scaffold next-steps, site `metadataBase`, and `llms.txt` examples now point at the custom domain.

## v0.3.0 — 2026-07-30

### ✨ Added

- Official plugins — first-party extensions shipped inside `okengine`, importable from `okengine/plugins` and attached with `.plug()`, built entirely on the public plugin API. The set: `securityHeaders()` (full helmet.js parity with API-first defaults — the complete secure-headers set on every response, failures included, plus a CSP builder with report-only mode), `cors()` (preflight `OPTIONS` answered at the edge even for paths bound to other methods; closed by default), `csrf()` (stateless fetch-metadata defense with an `Origin` fallback — no tokens, no cookies), `compression()` (native `Bun.gzipSync` for clients that accept gzip, with size and content-type thresholds), `maintenanceMode()` (one-flag 503 drain with `Retry-After`, allow-listed paths, and an operator bypass header), and `ipAllowlist()` (allow/deny rules by client IP at the edge of the pipeline). All no-op safely on non-HTTP triggers.

- `configSource()` — runtime-mutable plugin configuration: code as the floor, a DB row as the source of truth, and a KV binding as the automatic read-through cache. Every official plugin accepts one in place of static options; one clock-bound sync flow (declared effects, so the fx rule holds) refreshes the in-memory box that hooks read. Flip maintenance mode, open a CORS origin, or enable HSTS from the database — no redeploy.

- Plugin API: new `.edge(fn)` contribution — handlers for HTTP requests that match **no** flow (e.g. CORS preflight for a path bound to another method), run in install order before the plain 404, recorded as an `edge` intercept capability. The CORS plugin is built on it.

- Kernel: HTTP flows now serialize their outcome into `ctx.response` **before** the `onResponse` pipeline stage, so the last stage sees — and may replace — the final response, as `InvocationContext.response` always documented. Encoder input mirrors the app layer exactly; non-HTTP triggers are unchanged.

- Docs MCP: `oke dev` now boots the read-only docs MCP server next to the runtime MCP — app :6530 · Console :6533 · MCP :6535 · docs MCP :6536. No Bearer token (public documentation). Tools: `oke.docs.search`, `oke.docs.get` — agents answer "how do I … in OKE?" from the real pages instead of training-data memory. Docs content now ships inside the published package so the surface works in installed apps. Boot failure (missing content, busy port) skips the surface, never takes `oke dev` down.

- Documentation overhaul — all eight element pages rewritten to one standard (quick start → contract → per-environment drivers → troubleshooting). New sections: Reference (`oke.config.ts` options, environment variables, error codes, the full `fx` API, plugin API), AI Resources (runtime + docs MCP, agent contracts and skills, `llms.txt` endpoints), and a categorized Plugins section (Security · Operations · Performance). CLI Reference and Security moved under Reference.

### ♻️ Changed

- Vault: real OpenBao is now the `docker` / `prod` default driver (durable single-node Raft, real init/unseal, least-privilege app token). The previous age-based driver and its optional peer dependency were removed entirely. Single-point-of-failure by design: back up `.oke/openbao/unseal.key` to a separate safe location — losing it means losing every secret permanently, with no recovery.
