v0.16.0
✨ Added
Console — Auth & shell
- Access (
/access) manages API keys: create, edit, refresh expiry (same secret), revoke, rotate, reveal-once secret, usage from Runs, and a Bearer call example. Sidebar, command palette, and ⌘6. Session?next=may restore/access.
Runtime
fx.auth.createApiKey/listApiKeys/revokeApiKey/rotateApiKey/updateApiKey— session-only, issuer derived from the live principal. Effectsauth:api-keys.gate.authalways creates a shared API key store (HMAC-SHA-256 withgate.auth.secret). Host and Console share it viaattachHostToConsole.fx.store(db).select().from(table)infers the declared row fromstore.schema.table()/ Drizzle$inferSelect.select({ … })projections staySqlRow[]. ExportInferSelectRow.
💥 Breaking Changes
Runtime
- API key Bearer is the issuer:
fx.auth.userId/fx.operator.idand WideEventprincipalarecreatorId. The credential isfx.auth.apiKeyIdanddimensions.api_key. - Key secrets hash with HMAC-SHA-256 (
gate.auth.secret). Stolen table rows are not portable across apps. - Per-key
ipAllowlistandrateLimitare enforced on the HTTP Bearer path (401, noapi_keydimension).
Console — Auth & shell
- User-plane Access create requires
creatorUserId. The ceiling is that user's grants —console:*no longer adds application scopes. - Production no longer seeds a demo API key.
♻️ Changed
Console — Chrome
- Access sidebar and command-palette mark is BiometricAccessIcon so it no longer shares Vault's key glyph.
Console — Auth & shell
- Access create sheet uses plane / issuer / scope chips and expiry presets (never · 30d · 90d · custom
7d/12h). Issuer and scopes have in-header search. Scopes group by Module:Action prefix. User / Operator bands and the create-sheet Plane choices use UserIcon and CrownIcon. Rotate requires typed ROTATE. The inspector matches Vault: identity strip, usage tokens, ExplorerEmpty. - Access key inspector groups scopes by Module:Action, lists allow entries as IP/Host rows, frames the Bearer call like a Trace request, and labels past expiry as
expired. Revoked / unused / expired keys get a posture strip. Refresh resets the deadline from now (snaps 30d / 90d) without rotating the secret. - Access create and edit share expiry, an add-one allow list (IPv4, IPv6, localhost, or FQDN — junk is dropped), and a split rate (max / every / s·m·h·d·ms). Allow rows show IP vs Host. Edit can still change them after mint.
- Console default roles grant
memberonly. Demo User scopes follow the live Manifest (no leftover Skyportbooking:create/reports:exporton every app). - Invoke-as seeds ten identities from owner to guest. Demo User (
user_demo) and Member (user_member) keep their ids. Guest has no scopes. A Keel Manifest always seeds every Keel Gate scope (member,task:write,comment:write,files:write,project:admin,member:admin,webhook:admin) onto owner and the Access role ladder.
Runtime
- API key
ipAllowlistentries may be hostnames. Verify resolves them and matches the client IP (lookup failure is closed).
Dev, Keel & create-oke
- Keel flow helpers (
nextIdentifier,writeActivity,pushInbox,upsertTask) take the exportedFxtype — no hand-rolledfxshape and noas neverat the call site. - Keel seed featured members are a ten-rung owner → guest ladder and include
demo@example.comandmember@example.com.
Docs
- Flow and fx pages call out typing extracted helpers as
Fx. Store documents inferredselect().from(table)rows. - Gate documents API keys (issuer identity,
fx.authmethods, Access). fx lists the methods andapiKeyId. Vault points keys to/access.
🐛 Fixed
Console — Auth & shell
- Access create uses the issuer's identity plus role grants, and only offers scopes that exist in the app catalog.
- Access edit shows the frozen issuer and lists that principal's grantable scopes — not only the actor's user-plane ceiling. The sheet stays editable while the key list refreshes.
Runtime
FxAuthidentity (userId / scopes / apiKeyId) is distinct from key-management methods, socreateFxand wholesaleFxmocks typecheck. Store cache invalidation ignoresauth:api-keys. SQLselectoverloads keep inferred rows without implicit-any.- Edge
createFxand Store-onlyoke()loadfx.authkey methods only via computedimport.meta.require, so graphs that never manage keys stay under the existing bundle budgets. - API keys persist in public
oke_api_keyson the appstore.sql()connection (DATABASE_URL). A standalone restart hydrates a freshgate.auth.apiKeyStoreand Bearer still verifies — no Console. Access attaches that host instance, notoke_console.oke_api_keys.