0.17

Every published okengine release, newest first.

all series · 0.18 · 0.16

v0.17.2

✨ Added

Docs

  • SoftwareApplication JSON-LD now includes applicationCategory (DeveloperApplication) and a free MIT Offer (price 0, USD).
  • JSON-LD sameAs adds the live JSR package and X profile. Wikipedia, Wikidata, and LinkedIn are omitted — those profiles do not exist.

♻️ Changed

Docs

  • Homepage heading outline is H1 → H2 → H3 (readme/stack labels, element names, exports, surfaces, start-here cards) with no visual copy change.

v0.17.1

✨ Added

Docs

  • Homepage SoftwareApplication JSON-LD — name, description, and url taken from the published site metadata (not invented fields).
  • Markdown 404 body when Accept: text/markdown: links to /docs, /llms.txt, and /sitemap.xml. Status stays 404.
  • Homepage canonical /, og:type website, and /og/home image — same 1200×630 renderer as the docs Open Graph pipeline.
  • SoftwareApplication JSON-LD sameAs — GitHub repo and npm package pages already linked from the site chrome.
  • /llms.txt opens with **When to use this**: Introduction, Installation, AGENTS.md, MCP, CLI, errors, npm, and GitHub.

♻️ Changed

Docs

  • /changelog is an index of minor-version pages generated from changelog.md (one source, no forked copies) so each rendered page stays under the agent-readability budget.
  • HTML handbook URLs negotiate markdown in proxy.ts and send Vary: Accept. The site is no longer a static out/ export, because that Next.js mode cannot run the proxy.

v0.17.0

✨ Added

Console — Store

  • When tenancy.enabled, the Store tenant picker lists oke_tenants only — not historical run-history strings.

Runtime

  • gate.auth.tenant — tenant as an identity dimension (true or { required, source, header, resolve, authoritative }). Claim tid is trusted; header/subdomain require membership. fx.auth.listTenants / switchTenant / createTenant / upsertTenantRole are session-only (auth:tenants). switchTenant mints a new family and never Set-Cookies.
  • store.schema.policy.tenant(column) / store.schema.unscoped(), oke.tenant() RLS helper, KV {tenantId}: prefix, clock.perTenant, and vault.secret({ perTenant }) request-time paths.
  • **OKE1015** / **1016** / **1017** — tenant required, not a member, unknown or console:* tenant-role scope.
  • api.live(signal, input?, { onEvent }) subscribes to delivery: "live" HTTP SSE (callback + unsubscribe). Same handlers on exposing flows. Opt-in autoResubscribe reconnects with backoff and Last-Event-ID.
  • http.get(path).live(signal) / http.live(signal) expose a live signal as GET SSE. fx.live is the stream carrier. Multiple audiences of one signal boot when gates or match shape differ.
  • signal(..., { delivery: "live", retention: { maxAge, maxCount } }) caps the retained tape (AND-combined; omit for unbounded). Drivers prune on write and when live() opens.
  • Live SSE resume: reconnects send Last-Event-ID. A missing cursor is **OKE1014** / HTTP 410 LiveResumeGap — not a silent full replay. autoResubscribe then replays the remaining tape after backoff.
  • useLive(api, signal, input?) on okengine/client-react{ events, latest, error, isConnected }.

💥 Breaking Changes

Runtime

  • Turning on gate.auth.tenant is fail-loud: SQL tables need policy.tenant(...) or unscoped(); KV prefixes keys; vault contracts default per-tenant; flows default tenantScoped: true. Tenant-role scopes never melt into the JWT.
  • No-arg http.*.live(), flow({ live: true }), resource .live(), and Manifest live: boolean are gone. live is the signal name.
  • SignalBus.live() is an AsyncIterable of { id, payload } (not callback + unsubscribe). Duplicate (signal, gates, match) is **OKE1013**.

♻️ Changed

Runtime

  • Client gzip budget is 4 kB (was 3 kB) so api.live SSE subscribe fits the published cap.
  • Kernel edge gzip budget is 16 kB (was 15 kB) so Last-Event-ID resume (OKE1014 / 410) fits the edge encoder.

🐛 Fixed

Runtime

  • Store-only oke() lazy-loads live SSE resume (OKE1014 / 410) so apps that never declare Signal stay under the 51 kB gzip cap.
  • autoResubscribe: true waits with exponential backoff (500ms, doubling to 30s) before each reconnect so a closed SSE stream cannot tight-loop thousands of requests.
  • createClient(app) types .live(signal) flows as subscribe (onEvent), not JSON RPC — $routes now carries live / stream.
  • Store-only oke() lazy-loads the durable Flow runner so graphs that never run durable Flows stay under the 51 kB gzip cap after tenancy.

🔒 Security

  • Built-in oke_vault_secrets enables RLS with tenant_id = oke.tenant() OR tenant_id IS NULL (same helper as domain tables). ENABLE without FORCE so the Vault adapter still sees every row; Console / oke_app cannot list another tenant's ciphertext. Global NULL rows stay visible.