Pre-launch sweep: AI rate limiter, error allowlist, jsonb column registry, missing workflow column

PR #8 worked through code-review findings accumulated before the public launch. Seven commits touch rate limiting, safe error sanitization, tabular access optimization, streamed chat error handling, and a jsonb serialization bug that was silently dropping chat history. The last one is the most consequential.

securityinfrastructure

The chat_messages.workflow column was missing from the schema but the routes were already trying to write to it. Without the column, the insert failed, and because the insert was fire-and-forget, the failure was swallowed. Migration 010 adds the column; chat.ts and projectChat.ts were updated to check the insert error and return a 500 to the user rather than silently dropping their message. The same fix applies to both the single-document and project chat paths.

documents.ts got a safeRequestErrorDetail() function -- an explicit allowlist of error strings safe to pass through to clients ("filename is required", "Document not found", "Version not found", file-type mismatch patterns). Everything else collapses to a generic "Failed to ..." with a stable code field. A matching documentLoadError.ts helper on the frontend handles the codes. This is the defensive posture you'd want on any public API that wraps third-party storage.

The AI-route rate limiter adds MIKE_AI_RATE_LIMIT_WINDOW_MS and MIKE_AI_RATE_LIMIT_MAX (default 60 per 15 minutes) gating POST /chat, POST /projects/:id/chat, and the generate/regenerate-cell/chat paths on /tabular-review. Download links shifted from no-expiry to HMAC-signed tokens with a configurable DOWNLOAD_TOKEN_TTL_SECONDS (default 7 days).

postgresCompat.ts gained a jsonbColumnsByTable map listing every jsonb column across 10 tables. Insert, update, and upsert paths now call columnParam() which emits $N::jsonb casts for listed columns, fixing silent write failures on chat_messages.workflow, documents.structure_tree, projects.case_matter_metadata, and others. Finally, the streamed chat error handler now recognizes data.type === "error" events mid-stream and converts demo_budget_exceeded and insufficient_quota codes into specific user-facing messages.

So what The safe-error allowlist pattern in `documents.ts` is the most directly portable piece if your fork has a public API surface wrapping storage. The fire-and-forget insert fix is a concrete reminder of why silent failures compound quickly in chat persistence paths. The jsonb registry is worth adopting only if you're also running kveton's `postgresCompat.ts` shim.

View this fork on GitHub →

Spotted something wrong? Or know the PR text has fresher detail than the writeup above?

Commits in this thread

7 commits from CaseMark/mikeoss-casedotdev, oldest first. Source extracted verbatim from the harvested git log.

SHA Subject Author Date
3011eb79 Persist workflow metadata for chat messages kveton 2026-05-04 ↗ GitHub
d5539959 Harden document access and request limits kveton 2026-05-04 ↗ GitHub
4e770c9b Normalize document load errors kveton 2026-05-04 ↗ GitHub
a2842e05 Handle streamed chat errors kveton 2026-05-04 ↗ GitHub
e0f2d13d Optimize tabular document access checks kveton 2026-05-04 ↗ GitHub
3aadb6f8 Serialize jsonb values in Postgres compat writes kveton 2026-05-04 ↗ GitHub
8a72b816 Merge pull request #8 from CaseMark/codex/stream-error-review Scott Kveton 2026-05-04 ↗ GitHub
[codex] Handle streamed chat errors and harden document access

Capture this thread into my fork

Download a single Markdown prompt that tells Claude how to port every commit above into your working tree — adapting paths and structure to match your repo. Run it via claude -p < capture-thread-38.md from inside the repo you want the changes in.

⬇ Download capture-thread-38.md