Fork base: Grok provider, Supabase S3 path fix, JSONB bug, and per-user xAI keys

Custos's initial fork commit bundles four changes under a single "legalos" rebrand: a new xAI Grok provider, a Supabase-S3 compatibility fix, a JSONB query bug, and a per-user key extension. Unrelated changes, clean isolation, easy to split.

integrationmulti-tenant

The xAI provider lives in backend/src/lib/llm/xai.ts (~212 LOC). It's OpenAI-compatible - streaming and tool calling - registered as grok-4.3 in models.ts. The routing logic in llm/index.ts dispatches on model prefix: grok-* routes to streamXai/completeXaiText, same pattern as claude-* and gemini-*. XAI_MID_MODELS = ["grok-4.3"] slots it into the tabular model tier, and the frontend model dropdown adds an "xAI" group. The OpenAI SDK was already a dependency upstream, so no new dependency.

The Supabase-S3 patch in lib/storage.ts reads region from env (R2_REGION, defaulting to "auto") and sets forcePathStyle: true on the S3 client. Supabase Storage exposes an S3-compatible endpoint that requires path-style requests; Cloudflare R2 works without it. If you've switched from R2 to Supabase Storage and hit credential errors, this is the fix.

The JSONB containment bug: GET /projects was passing a JS array directly to the Supabase ? containment operator, which needs the value JSON-stringified. Results in a 500 for any user with shared projects. Small fix, real bug.

d9295771 extends the per-user API key pattern to xAI. A new user_profiles.xai_api_key column (migration 005_user_xai_key.sql) mirrors the existing claude_api_key/gemini_api_key structure. getUserModelSettings and getUserApiKeys are updated to return xai. The env XAI_API_KEY stays as a tenant-level fallback - if neither is set, calls fail at runtime rather than at configuration time.

So what The Supabase-S3 path fix and JSONB bug are low-risk, targeted pulls for anyone running those backends. The Grok provider is well-isolated - a single new file plus registration in three small places - and straightforward to lift if you want Grok in your model menu. The xAI model availability check in the frontend is overly permissive: it shows Grok as available regardless of whether any key is configured, so users without a key will get a runtime failure rather than a disabled button.

View this fork on GitHub →

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

Commits in this thread

2 commits from Custos/legalos, oldest first. Source extracted verbatim from the harvested git log.

SHA Subject Author Date
6ee8547e Fork as legalos: xAI Grok support, Supabase S3, and bugfixes Custos 2026-05-04 ↗ GitHub
commit body
- Add xAI provider (grok-4.3) with OpenAI-compatible streaming + tool calling
- Add model dropdown to TabularReviewView header (persists tabular_model preference)
- Patch storage.ts for Supabase S3 (region from env, forcePathStyle)
- Fix GET /projects 500: jsonb contains needs JSON-stringified array
- Update README with attribution to upstream willchen96/mike under AGPL-3.0
d9295771 Per-user xAI API keys Custos 2026-05-04 ↗ GitHub
commit body
- New user_profiles.xai_api_key column.
- userSettings reads/returns xai key alongside claude + gemini.
- UserProfileContext exposes xaiApiKey + updateApiKey('xai', ...).
- Account → Models page renders an xAI key field.
- Tabular review xAI dropdown stays available without a per-user key
  (env XAI_API_KEY remains the fallback) but a user key takes precedence.

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-45.md from inside the repo you want the changes in.

⬇ Download capture-thread-45.md