[pull] main from danny-avila:main#150
Merged
Merged
Conversation
* ci: Sync Helm chart tags * ci: Address Helm tag sync review
* chore: upgrade docker builds to node 24 * test: avoid array at in telemetry spec
* chore: upgrade vite for node 24 * fix: restore production vite boot * fix: preserve dynamic pwa shell
The ROLES cache is a single process-wide store, but role documents are
per-tenant (unique index { name, tenantId }). getRoleByName checked the cache
by role name BEFORE the tenant-scoped DB read, so a warm entry written under
one tenant's context was served to another tenant — leaking that tenant's
permission bits into the other's authorization decisions.
Scope every ROLES cache key with scopedCacheKey(), which appends the active
tenantId from the AsyncLocalStorage tenant context. It is a no-op when no
tenant context is set (or under runAsSystem), so single-tenant deployments
behave exactly as before.
Adds role.cache.spec.ts with a real Map-backed cache: two tenants sharing a
role name receive their own permissions, the cache key is tenant-scoped, the
same-tenant fast path still avoids a second DB read, and single-tenant mode
still uses the unscoped key.
…3455) getAppConfig caches per-principal merged config overrides under a key built by overrideCacheKey(role, userId, tenantId). The key used the tenantId *argument* only — but callers that go through the tenant middleware (the common path) pass no explicit tenantId and rely on the AsyncLocalStorage tenant context. Those calls were keyed under the shared '__default__' bucket, so the DB query (correctly scoped to the ALS tenant by the Mongoose plugin) produced a merged config that was then cached and served to the next tenant resolving the same role/user — leaking model specs, endpoints, and interface flags across tenants. Fall back to getTenantId() before '__default__' so the cache key reflects the actual tenant scope (param or ALS). Tighten the strict-mode warning to fire only when there is genuinely no tenant anywhere (param nor ALS), since the ALS case is now scoped rather than defaulted. No-op for single-tenant deployments, where getTenantId() is undefined and the key stays '__default__'. Adds tests (real Map-backed cache) proving the ALS tenant scopes the key and that two tenants resolving the same role each get their own config with no cache collision.
Mongoose's distinct query operation was not in the tenant-isolation plugin's hooked-operation list, so .distinct() (and .find(...).distinct(field), whose op switches to 'distinct') ran unscoped — reading across all tenants. This affects the ACL resource lookups (findAccessibleResources, findPublicResourceIds — including PUBLIC 'shared-to-all' entries), agent category values, and random prompt categories. distinct IS a registerable query-middleware hook in Mongoose 8 (it is in queryOperations), so the fix is to register the existing queryMiddleware for it — one line. This keeps every call site as .distinct(), which is the established FerretDB-compatible pattern (getRandomPromptGroups was deliberately built on .distinct() rather than an aggregation stage for FerretDB support), and scopes all distinct queries systemically with the same SYSTEM-context bypass and strict-mode fail-closed behavior as the other operations. Adds distinct cases to the plugin spec (including the find().distinct() op switch and SYSTEM/no-context paths) plus behavioral tenant-isolation specs for the ACL and category lookups; verified all fail without the hook.
Adds a coverage test that enumerates all registered models and asserts every
schema with a tenantId field has the tenant-isolation plugin applied (detected
via the global Symbol.for('librechat:tenantIsolation') marker the plugin sets),
with a single documented allowlist entry — SystemGrant, which scopes tenancy
manually. A future model that ships a tenantId field without the plugin (the
exact gap that lets data leak across tenants) now fails CI instead of shipping
silently.
* feat: use SecretInput for sensitive fields * fix: align auth SecretInput styles * chore: remove unused password i18n keys * fix: align SecretInput controls * fix: use SecretInput for dynamic credentials * fix: reveal SecretInput controls on hover * fix: align SecretInput eye icon and modernize controls The wrapper was a flex container, so passing 'mb-2' on the input made it contribute its margin to the wrapper's cross-axis size — the controls overlay spanned the inflated height and centered the toggle 4px below the input's true center. Switching the wrapper to a plain relative block collapses height back to the input. Also tightens the toggle/copy buttons (size-7 rounded-md with hover:bg-surface-hover) and adds a focus ring on the input. Auth pages still override className/buttonClassName so login/register styling is unchanged. * fix: remove focus ring from SecretInput * fix: keep green focus border on auth secret inputs SecretInput's modernized default uses focus-visible:border-border-heavy and hover:border-border-medium, which Tailwind emits after the auth pages' focus: rules and overrides them. Auth pages now also declare focus-visible:border-green-500 and hover:border-border-light so cn()/twMerge resolves them as the winners when classes are concatenated. * feat: add optional sensitive flag to MCP customUserVars Dynamic MCP credential fields all rendered as masked SecretInputs, which also hid non-secret setup values like usernames, project keys, and URLs. Add an optional `sensitive` flag to customUserVars and the plugin auth config. It defaults to masked when omitted, so existing configs keep the safe-by-default behavior; set `sensitive: false` to render a field as plain text. The flag is display-only — values remain encrypted at rest.
* fix: Preserve custom endpoint reasoning params * fix: Address custom reasoning review cases * fix: Format configured reasoning defaults * fix: Honor dropped reasoning params * fix: Configure custom reasoning response key
…ability Paths (#13461) * chore: reduce auth and balance operational noise * chore: tighten balance and capability noise handling * chore: avoid balance 404s when disabled * chore: use response locals for balance handoff
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to subscribe to this conversation on GitHub.
Already have an account?
Sign in.
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
See Commits and Changes for more details.
Created by
pull[bot] (v2.0.0-alpha.4)
Can you help keep this open source service alive? 💖 Please sponsor : )