Skip to content

v0.6.5

Choose a tag to compare

@mkjt2 mkjt2 released this 15 Jun 16:20
cc1581b

Security-focused release: token-at-rest hashing, OAuth PKCE, admin hardening, and session idle timeouts, plus OpenCode transcript parity and a broad refactoring pass.

DB Migrations

  • 000053 — hash_session_device_tokens (#306): web_sessions.id and device_codes.device_code are now stored as sha256(token) (hex) instead of the raw value, so a DB read (backup leak, SQLi, replica, support tooling) cannot replay live session/device tokens. The lookup-key format is not backward compatible across the migrate-then-deploy gap, so the migration deletes all rows in web_sessions and device_codes rather than hashing in place. Impact: every user is logged out and must re-login once. The demo shared session is re-provisioned by bootstrap on next start; any in-flight device-code flow expires within its 5-minute window. No column type change.
  • 000054 — web_session_last_activity (#314): adds nullable web_sessions.last_activity_at TIMESTAMP as a sliding idle gate alongside the absolute expires_at cap, with a one-shot backfill (last_activity_at = created_at). The idle predicate uses COALESCE(last_activity_at, created_at) for rollout-gap safety.

API Changes

  • OAuth login now uses PKCE (S256) (#313): all three login endpoints (GET /auth/{github,google,oidc}/login) generate a code_verifier stored in an HttpOnly oauth_verifier cookie (MaxAge 300) and send code_challenge + code_challenge_method=S256. Callbacks read and clear the single-use verifier cookie (rejecting with 400 if absent) and include it in the token exchange. No client action required.
  • Admin authorization is now a union (#308): Admin = SUPER_ADMIN_EMAILS (env) OR the new users.is_admin column. New endpoints POST /api/v1/admin/users/{id}/grant-admin and .../revoke-admin toggle the column at runtime (no env edit + restart). Revoke is guarded against orphaning the last effective admin (returns 409); granting a read_only user is rejected (400). User-list rows now carry is_admin (raw column) and is_super_admin (in env); GET /api/v1/me returns is_admin as the union of both.
  • Typed-confirmation echo on destructive admin actions (#323): POST .../users/{id}/deactivate now requires body { "confirm": "<target email>" }; DELETE .../users/{id}?confirm=<target email> requires the email as a query param. Both are compared (trim + case-insensitive) server-side and rejected (400) on mismatch, after the last-admin guard. The card-invalidation endpoint now requires confirm to equal the freshly re-counted affected-session count on execute (dry_run: false), binding the action to the current blast radius. The last-effective-admin guard returns 409 and runs before any S3 wipe.
  • Pricing doc gains cacheWrite1h (#309): GET pricing payload now exposes cacheWrite1h (1-hour cache-creation rate, 2x input for Claude) alongside cacheWrite (5-minute rate). Absent/0 falls back to cacheWrite so older/remote docs never bill 1h tokens at $0.
  • Cost Distribution lowest band widened (#320): the histogram's two sub-$1 decades are merged into a single $0.01 – $1 band; from $1 up it remains one band per power of 10.

Security

  • Hash web-session IDs and device codes at rest (#306) — see DB Migrations.
  • OAuth 2.0 PKCE (S256) on GitHub / Google / OIDC (#313).
  • Admin hardeningSUPER_ADMIN_EMAILS startup validation + last-admin guard (#315); is_admin column made load-bearing (union) + admin tag UI (#308); server-verified typed confirmation on destructive admin actions (#323).
  • Web session idle timeout (sliding window) + 7-day cap (#314).
  • Cross-origin guard on /auth/cli/authorize + /auth/device/verify (#307).
  • OAuth→existing-account email auto-linking now defaults OFF (#305).
  • Device-verify brute-force lockout + remove user-code modulo bias (#304).
  • Redact git_info for non-owner session access (#303).
  • Stop leaking owner email to anonymous viewers of public shares (#302).
  • Remove dormant request/response body-logging middleware (#301).
  • Build hardening — Dockerfile -trimpath + govulncheck CI gate (#298).
  • TRUSTED_PROXY_HEADERS allowlist + rate-limiter bucket cap (#297).

Features

  • OpenCode transcript Cmd-F search (shared search toolkit) (#318).
  • OpenCode transcript cost side-rail (shared CostBar) (#317).
  • OpenCode transcript minimap/timeline bar (shared TimelineBar) (#316).
  • Split cache-creation billing by ephemeral tier (5m vs 1h) (#309).
  • Self-host onboarding overhaul — canonical zero-config compose, Caddy profile, startup guard (#300).

Bug Fixes

  • Default Content-Type on body-less mutating requests (#322).

Refactoring

  • useCardState hook for the repeated card loading/error guard (#331).
  • Extract generic ProviderFilterDropdown (#330).
  • Generalize transcript filter hooks across all three providers (#329).
  • Extract shared useApiData hook from useTrends + useOrgAnalytics (#328).
  • Collapse analytics/store.go card get/upsert into generics + registry (#326).
  • Dedupe transcript error-reporting (Claude/Codex services) (#327).
  • Dedupe ToolsAnalyzer tool counts by tool_use.id (#325).
  • Split oauth.go into per-protocol files (#324).
  • Deep package-by-package backend maintenance pass (#319).
  • Data-drive TrendsTokensCard cache-write row, drop the provider allowlist (#312).
  • Align per-session TokensV2Card layout with the Trends tokens card (#311).
  • Add opencodeAdapter.test.ts for Claude/Codex test parity (#310).
  • Migrate per-session cost readers from flat session_card_tokens (v1) to tokens_v2 (#295).
  • Contract test pinning backend↔frontend getModelFamily parity (#296).

CI

  • Retry backend integration go test up to 3x in CI (#321).

Docs

  • Reconcile backend/.env.example with CONFIGURATION.md env-var reference (#299).

Breaking Changes

  • All users are logged out on upgrade — migration 000053 deletes all web_sessions and device_codes rows (token-at-rest hashing). Every user re-logs in once; in-flight device-code flows must restart.
  • OAuth email auto-linking now defaults OFF (#305): a new OAuth login whose email matches an existing account is no longer auto-linked by default. Self-hosters relying on the old behavior must opt back in via configuration.
  • New TRUSTED_PROXY_HEADERS env var (#297): client-IP / proxy headers are now read only from an explicit allowlist. Deployments behind a proxy that depend on forwarded headers must set this; otherwise those headers are ignored.
  • SUPER_ADMIN_EMAILS is now validated at startup (#315): a malformed value fails fast at boot instead of being silently ignored.