Highlights
Deleted records go to the trash, and can be restored
With the Labs settingENABLE_SOFT_DELETE on, a delete marks a record instead of removing it. This covers user deletes, KB folder deletes and connector sync deletes. The record keeps its edges, permissions and stored content, and it disappears from search and listings (#3695, #3718). A deleted batch can be restored exactly as it was (#3799). Collections get a Recently deleted page (#3884). An hourly job in the connectors service, run by one replica at a time, purges records once they have been in the trash for the retention period: 14 to 28 days by default, set by softDeletePurge in the platform settings (#3847). Trashed folders refuse moves, creates and uploads (#3973). Tables in the trash no longer show up in the full-record tool’s foreign-key lists (#3903). A vectors-cleanup event the broker refuses counts as unpublished (#3901). Records stored before the isDeleted field existed count as live (#3800, #3825). The restore refusal field is now blockedRecordId (#3986 — @siddhant-pipeshub). The whole checklist is tested end to end (#3853, #3885). With the setting off, deletes work as they did before. (@shekharkadyan)
Agents can search the knowledge graph
A newentities vector collection holds one point for each taxonomy node, record title and record group. It is written by the indexing pipeline, and every result is permission-checked against the graph. Agents get search_entities and find_records_by_entity, and search accepts entity_ids. So an agent can go from “what’s tagged Legal” to the records themselves. Extracted categories, topics and languages now resolve to one canonical node per org (#3308, #3729 — @tushar1245). Follow-up phases add these:
- rebuilding the entity index from the graph (#3759)
- org-scoped aliases (#3762)
- a reversible merge of duplicate taxonomy nodes (#3767)
- retried entity cleanup when a connector is deleted (#3769)
- the permission check inside the graph query (#3770)
- concurrent enrichment and alias healing (#3771)
- an entity-resolution eval (#3772)
- top-3 merge candidates (#3773)
- further fixes (#3757, #4019) — all @abhishekgml1991
AI models: discovery, multi-model add, and prompt caching
Admins can fetch a provider’s model list and add several models in one step, in settings and in onboarding. Each model is checked and saved on its own. Models that pass are kept, and failed ones can be retried. Rotating the key updates every model on the same connection. The release also adds an optional OpenAI base URL and OpenAI-compatible image, speech-to-text and text-to-speech models. Configured models are listed at an OpenAI-compatibleGET /v1/models (#4012, #4054 — @abhishekgml1991).
The agent loop now uses provider prompt caching for the prompt and the tool definitions: Anthropic breakpoints, Bedrock cache points, and OpenAI/Azure prompt_cache_key. The prompt prefix also stays the same from turn to turn, so the cache can be reused. It is on by default and controlled by PIPESHUB_PROMPT_CACHE (#4038 — @abhishek-pipeshub). A model can carry its own default reasoning effort, which the chat picker shows (#3828, #3837).
A locked-down code sandbox and MCP
The sandbox and MCP changes below affect how operators deploy.- Docker access: The app container no longer mounts the Docker socket. A new
docker-socket-proxyservice holds it and allows only sandbox-image containers with no privileged mode, binds or host namespaces. The app now runs as uid 1000 and keeps its existing paths (#3782, #3811, #3816, #3820). The Helm DinD sandbox goes through the same proxy (#3888 — @abhishekgml1991). - Network: Sandbox code runs with the network off by default, and when it is on, egress goes through a firewall (#3780).
- Sandbox mode: An unset or unknown
SANDBOX_MODEnow fails closed (#3725 — @rish231294).localmode requiresSANDBOX_ALLOW_LOCAL=true(#3803). Symlink tar members are dropped, and the sandbox user and file-size limit are pinned (#3810). All of these are by @vishwjeet-pawar03 unless noted. - MCP: Custom STDIO MCP servers run only when the operator sets
MCP_ALLOW_CUSTOM_STDIO=true, and catalog commands are pinned (#3887 — @abhishekgml1991).
Connectors remove what the source deleted
A long-running gap is closed. Many connectors kept records whose items had been deleted at the source, or that a narrowed filter now left out. This release fixes that for these connectors:- BookStack (#3704)
- Confluence Cloud and Data Center, team and personal (#3705, #3719, #3721, #3823)
- Zammad (#3706)
- Nextcloud (#3707)
- S3-style object stores, including the “Not in” filters (#3708, #3716, #3717)
- Google Drive Workspace (#3709)
- Web (#3712)
- Jira personal (#3934), and Jira on Free plans, which refuse the audit log (#3875, #3905)
- Snowflake (#3688)
Indexing: a lane for each connector, and less work per record
A community user reported that their Slack connector waited behind about 200,000 GitLab events, because both connectors hashed to the same Redis Streams lane. Each connector now gets a lane that is recorded when the connector is created, freed when it is deleted, and separated once if two collide.FAIR_SCHEDULING_LANE_ASSIGNMENT now defaults to assigned on Redis (#3979, #3980, #3982). On Kafka, a connector at its cap no longer holds up the connectors that share its partition (#3977). Queued events of a connector that was turned off or removed are settled as they are read (#3976). Turning a connector back on re-queues what pausing it parked (#4026). A rate-limited item waits for the reset instead of failing (#4039).
There is also less work per record. Extraction is sent only what it reads, and each saved record is encoded once (#4047). Each record needs fewer graph queries, and background sweeps are limited to a tenth of a busy loop (#4048). Qdrant upserts are sent as a Batch (#4043). Large-file parsing stays off the event loop (#3945).
An All Artifacts gallery, and queued email
The new All Artifacts (Beta) gallery lists the artifacts a user can access. Users can preview them, download them and switch between versions, and artifacts no longer mix into All Records (#3427). The gallery renders PPTX files (#4056) and gets apply and clear controls for its filters (#4058) — all @abhishekgml1991. Invite emails are no longer sent during the HTTP request. They go out through the message broker, and a background consumer sends them over pooled SMTP (#2906 — @anupPradhan0). The mail consumer is fixed so it does not send duplicates or lose a send (#3743 — @abhishekgml1991).Breaking changes & upgrade notes
- Coding sandbox:
SANDBOX_MODEmust be set. If it is unset or has an unknown value, the sandbox is now unavailable instead of falling back tolocal(#3725).localalso needsSANDBOX_ALLOW_LOCAL=true(#3803).SANDBOX_ALLOW_NETWORKnow defaults tofalse(#3780). Docker Compose installs gain adocker-socket-proxyservice under thesandboxprofile. The app reaches it throughDOCKER_HOST=tcp://docker-socket-proxy:2375and runs as uid 1000, so custom volume mounts must be writable by that user (#3782). Helm DinD sandboxes go through the proxy too (#3888). - MCP: existing custom STDIO MCP servers stop running until the operator sets
MCP_ALLOW_CUSTOM_STDIO=true(Compose.envor Helm values). STDIO env names on the denylist (PATH,LD_*and others) are refused (#3887). - Service-to-service auth: the parsing and extraction services require a service token (#3818). The query and connector services no longer send wildcard credentialed CORS (#3890).
- Redis Streams lanes:
FAIR_SCHEDULING_LANE_ASSIGNMENTdefaults toassigned. Set it tohashon every service to go back to hashed lanes. Events already on a stream are not moved: a connector gets its lane on its first publish after the upgrade, and anything already queued finishes on its old lane. Kafka is not affected (#3982). - Invites:
POST /users/bulk/invitenow returns{ "queued": true }once the emails are accepted for delivery. A200no longer means they were delivered (#2906). - API status codes and shapes:
- A member who calls an admin-only route gets
403, not400(#3696, #3827). - Stats on a connector the caller can’t open return
404, not403(#3922). - A sync toggle or resync for such a connector returns
404, not500(#3955). - A resync that can’t be queued returns
503(#3910). - Malformed storage ids return
400, not500(#3668). - The restore refusal field
record_idis nowblockedRecordId(#3986). - Two knowledge-base record-creation routes on the connectors service, which could only answer
500, are removed (#3933).
- A member who calls an admin-only route gets
- Downloads and secrets:
- The connectors service’s signed download URLs are bound to the user who redeems them, and still expire after 60 minutes (#3723).
- On the storage service’s internal download route,
expirationTimeInSecondsis capped at 7 days. The default is still 1 hour, and a longer value is refused with400(#3790). - Connector config responses mask stored secrets and no longer return the owner’s tokens (#3917, #3923). Admins can read secrets back through a dedicated route (#3948).
- Toolset credentials are hidden from members and masked for admins (#3775).
- Python dependencies: FastAPI moves to 0.136.3, and Starlette is pinned to 1.7.0 (#3817). This changes what callers of the Python service ports see directly. The gateway, UI, Slack and MCP already send the right headers, so they are not affected.
- JSON requests without
Content-Type: application/jsonget422instead of being parsed. - URL-encoded forms are limited to 1000 fields and 1 MiB per field, and a form over either limit gets
400. Multipart uploads are not limited. - CORS responses echo the request
Originand always sendVary: Origin.
- JSON requests without
Connectors
- Google Drive & Gmail
- Drive supports external users (#3115 — @R0gue-one), and syncs files that a full listing used to skip (#3678 — @abhishekgml1991).
- Sharing a file in a filtered folder no longer deletes it (#3873).
- Gmail web URLs are fixed (#3487 — @R0gue-one).
- Gmail reads its own OAuth app when it opens a Drive attachment (#3829), and asks only for read-only scopes when it impersonates a Workspace mailbox (#3833).
- Google toolset tokens refresh with their consent scopes, and the Gmail Drive-download fallbacks are fixed (#3838).
- Calendar’s consent no longer asks for
gmail.send, and Meet’s addsmeetings.space.readonly(#3912). - A misleading Drive Workspace log line is corrected (#3835).
- Salesforce
- Sandbox and My Domain orgs sign in on their own login host (#3848), and the setup form suggests a My Domain instance URL (#3830).
- The Login URL error is now a fixed message written for people (#3904 — @tushar1245, #3906).
- A file’s stored extension is read from its file record (#3647).
- Cached search access is cleared when permission edges are rewritten (#4040).
- Confluence — admin date filters apply as set, and the 24-hour look-back runs only from the sync checkpoint (#4042). Folder ancestors are recognised in the placeholder sweep (#3649). “Grant Jira user access” survives toolset registration (#3881).
- Zammad — ticket search pages through
/tickets/search, so Zammad 6.4 reads every ticket once (#3807). Every knowledge-base answer is read on Zammad 6.0–6.4 (#3839). Each ticket gets one search window, so a resumed read doesn’t read its ranges again (#3876). - Linear — a page that times out or is rate-limited is requested again (#3991). A team whose pages never came back is reported, and the deletion checkpoint stays where the scan stopped (#3993).
- Web — the connector recovers from a browser crash (#3815 — @R0gue-one).
curl_cffistreaming no longer crashes the connector service (#3755). A page request that never finishes is abandoned and its thread is stopped (#3842). A page keeps its retry when the headless fallback fails (#4004 — @abhishek-pipeshub). - SMB — the SMB connectors are fixed (#3957 — @R0gue-one), and a renamed file keeps its record (#3645).
- Databases — Postgres tests the credentials before the setup form moves on, and shows PipesHub’s IP (#3985 — @tushar1245). MariaDB skips tables whose content hasn’t changed (#4028). The Redshift driver no longer evaluates
int2vectorcolumn text (#3895 — @abhishekgml1991). - Slack — channels sync when the token has no DM scopes, and listing errors fail loudly (#4025). The Slack bot accepts Markdown, text, CSV and TSV attachments (#3683).
- GitHub & GitLab — GitHub rate limits are treated as rate limits, and some quota is left for the user (#4032). GitLab streams items whose titles have characters outside latin-1 (#3938).
- Stored-field fixes after the file-record split
- SharePoint reads the stored file hash from the file record (#3646).
- Box reads a shared item’s stored path from the file record (#3648).
- Nextcloud finds a file’s folder by path (#3650).
- Local FS reads the file record before retiring one (#3651).
- Local sync treats the sync root as a literal path (#3898 — @abhishekgml1991).
- An identical object-store copy gets its own record (#3713).
- Smaller connector fixes
- OAuth registrations
- Connector platform
- Connector sync is refactored (#3128 — @shrikantNemiwal1).
- Case-only renames are allowed, and OAuth app authorship is recorded (#3583 — @vishwjeet-pawar03).
- A connector is marked
DELETINGbefore its delete is published (#3840 — @tushar1245). - A failed toggle no longer undoes someone else’s newer change (#3915 — @R0gue-one).
- The crawling manager derives the connector type from the instance (#3658 — @darshan-pipeshub).
- The graph DB status is reverted when sending a message fails (#3641 — @R0gue-one).
- Folder path filters allow spaces and commas (#3952 — @abhishekgml1991).
- A deleted connector’s schedule is removed even when its config can’t be read (#3908).
- Filter options are capped at 100 (#3910), and
/configuredand/agents/activepage with a true total (#3909). - A personal connector is listed only for its creator (#3921).
- A failed group member removal raises an error instead of returning False (#3937).
- A connector record and its parent edge are deleted in one write (#3994).
- Admins are told what disabling sync leaves searchable, and that content stays searchable in exact-text search (#4041).
Agents & AI
- Storage Pattern Match — LLM-driven grep over stored records, scoped to what the user can access (#3568 — @vishwjeet-pawar03). Same-named folder and page twins are kept apart (#3761 — @vishwjeet-pawar03). Record JSON is stored as UTF-8, so non-ASCII text matches (#3763 — @abhishekgml1991). Command output and search terms stay out of the info log (#3894 — @abhishekgml1991). A cited grep hit keeps its name, type and quote (#4014 — @abhishek-pipeshub).
- Agent loop
- A run that reaches its turn limit answers from its last tool results (#3808 — @tushar1245).
- The agent is told when a withheld tool becomes callable (#4015 — @abhishek-pipeshub).
- A mistyped record id points back at records already shown, including storage-search results (#4016, #4030).
- The chat ask-user-question flow is fixed (#3592 — @Pranjal-wq).
- Agent saves
- Search
- Hits from records without a link, such as SQL tables, are kept (#3865).
- Search falls back to the original query when the AI rewrite fails (#3931).
- One stalled provider call no longer holds a search for minutes (#3972).
- Records shared with a role are found on Neo4j (#3710).
- Cached access is cleared as soon as a connector removes a permission (#4031).
- A Jira issue key matches exactly (#3914).
- AI model settings
- Rejected settings get a clear 4xx, the default model is recorded, and follow-up length is limited (#3701).
- The vector store stays sized for the embedding model in use (#3697).
- The model health check no longer crashes (#3805), and stops telling admins to uncheck Multimodal over a bad key (#3813 — @abhishekgml1991).
- Tools — Jira
get_issuereturns the ticket description (#3911 — @darshangodase). Slack tools get permissions (#3722 — @jatingaur18).
Fixes
- Knowledge bases & collections
- A folder delete stays inside the folder (#3679).
- Duplicate-name checks read the file type from the record (#3802).
- A move is one write on Neo4j (#3956).
- A colliding create is retried (#3990).
- The default knowledge base create converges, and a half-built one is healed (#3996).
- App browse works on ArangoDB again (#3795).
- A file’s size shows when it is kept only on its file node (#3801).
- Long list names show in full (#3724 — @Pranjal-wq).
- A lock shows when a collection group permission is off (#3676 — @Pranjal-wq).
- Collection frontend fixes (#3760 — @darshan-pipeshub).
- Users, groups & teams
- Service accounts are offered in group and team member pickers (#3627).
- Group names are unique per org, checked on create and rename and enforced in the database (#3638, #3698).
- A team create converges on one team (#3997).
- An empty users lookup is skipped, and a bad id gets a plain explanation (#3687).
- An email edit syncs to the graph (#3436 — @Pranjal-wq).
- Mail — the email-change link now says it lasts 20 minutes, not 14 days (#3692).
- Storage — every caller reads the storage config from the internal route (#3918 — @tushar1245). A missing document on internal routes logs at warn (#4022 — @abhishekgml1991).
- Frontend & desktop — Electron login is fixed (#3602 — @R0gue-one). A notification reloads its conversation even when that conversation is already open (#3946 — @abhishekgml1991).
- Parsing — Windows-1252 text is read before falling back to Latin-1 (#3714).
Performance & reliability
- Graph writes are atomic and fail loudly
- Multi-step Neo4j sync writes are single statements (#3924).
- Record permission writes are atomic on Neo4j (#3932).
- Agents, teams and knowledge bases are created all-or-nothing (#4046).
- A fenced single-field write locks the record first (#3988).
- ArangoDB write-write conflicts are retried (#3794), and a refused REMOVE fails the delete so it rolls back (#3926).
- Deleting a collection record removes all its edges (#3874).
- A mail’s attachments are deleted with it on Neo4j (#3879).
- Failed reads raise an error instead of returning None (#3672, #3703).
- A sync point is deleted by its key (#3666).
take_back_kept_record_groupis defined once (#3920).- A Neo4j team KB is found when the list is searched by name (#3925).
- Service accounts can attach files to chat on ArangoDB again (#3702).
- Indexing status
- A duplicate takes its twin’s final enrichment state (#3878), and its status is written last so a failed attach is retried (#3594 — @shrikantNemiwal1).
- A record is marked indexed with an update, not an upsert (#3919 — @shrikant-pipeshub).
- Only records whose event the queue has moved past are re-published (#3944).
- Inheritance edges that are removed are counted in the write itself (#4035).
- Services
Security & authentication
- Advisories — signed download URLs are bound to the user who redeems them (GHSA-cf48-qh3f-g3g9). The sandbox fails closed when
SANDBOX_MODEis unset (GHSA-x783-vmqc-7jc9). Ids are encoded where they go into service URLs (GHSA-rfmg-j28j-f635). (#3723, #3725, #3728 — @rish231294) - Sessions & sign-in
- The
refresh_tokengrant is bound to the authenticated client (#3639 — @rish664). - The SAML session cookie is sent only over HTTPS (#3637), and SAML no longer keeps an in-memory login session (#3700).
- SAML security fixes (#3765 — @R0gue-one).
- Ended sessions are refused at the Python services (#3680).
- Password reset links work only once, even when used twice at the same moment (#3681).
- A deleted or disabled user’s refresh token gets a plain 401 (#3686).
- Sign-in attempts are counted before the comparison, and
returnTois validated (#3789 — @R0gue-one).
- The
- Access control
- Attachment reads are checked for virtual record ids and for history attachments (#3633 — @Pranjal-wq, #3764 — @uncleSlayer).
- Record updates in knowledge bases are permission-checked (#3640 — @uncleSlayer).
stream_recordand record delete are scoped to the caller’s org (#3675, #3667 — @tushar1245), and so are connector lists and lookups (#3913).- Org-wide schedule routes are admin-only, and queue stats are scoped to the org (#3814 — @darshangodase).
- Record delete, connector config read and AI model endpoint checks are tightened (#3788 — @shrikantNemiwal1).
- Domain and “anyone” shares grant no access, and deleted users leave KB sharing lists (#3691).
- Agents —
execute_sql_queryis limited to allowed connectors (#3677 — @tushar1245). An agent’s sources stay within its configured knowledge (#3751 — @abhishekgml1991).xargsargument injection in Storage Pattern Match is blocked (#3784 — @vishwjeet-pawar03). - SSRF & untrusted input
- Internal addresses are blocked when fetching document images (#3632 — @darshangodase).
- Web and RSS fetches stay off non-public addresses (#3674 — @siddhant-pipeshub).
- Model endpoints are checked at connect time (#3892 — @abhishekgml1991).
- Untrusted images are no longer decoded, and empty OAuth scopes are rejected (#4055 — @abhishekgml1991).
- Regex metacharacters in user and group search are escaped (#3804 — @darshangodase).
- pdf.js eval is turned off (#3661).
- Chat asks for a click before it loads images from other sites (#4011 — @Pranjal-wq).
- Slack — the internal Slack config route requires a bot-only token (#3787), and the org is never guessed when hydrating a Slack caller (#3783) — @Pranjal-wq.
- OAuth & secrets
- The allowed subset of the requested scopes is granted, and
usergroup:readis added toMCP_SCOPES(#3731 — @uncleSlayer). - Scopes for
createConversationanduploadRecordsare aligned (#3900 — @siddhant-pipeshub). - Shared OAuth app credential trust is fixed (#3768 — @shrikantNemiwal1).
- Toolset credentials are hidden or masked, and model health checks are admin-only (#3775 — @siddhant-pipeshub).
- The allowed subset of the requested scopes is granted, and
- Hardening
- Client-side IP forgery against the rate limiter is closed (#3631 — @uncleSlayer).
- Server errors return fixed text instead of exception text (#3819 — @shrikantNemiwal1).
- Nodemailer is upgraded to 10.0.14, and file or URL attachments are refused (#3897 — @abhishekgml1991).
- onnxruntime telemetry is turned off in every service (#4060 — @abhishekgml1991).
- Dependency patches
Developer experience & infrastructure
- CI & release
- Every third-party GitHub Action is pinned to a commit SHA (#3685).
- Integration tests run on
pull_requestwith secrets kept away from forks (#3886 — @abhishekgml1991). - Integration-suite guards run as their own required job (#4010, #4036 — @abhishek-pipeshub).
- CI images are built from GHCR base images (#4045).
- The sandbox image is published with the app image (#3995).
- The changelog skill runs under
workflow_dispatch(#3659). - New F811 redefinitions in tests fail CI (#3852).
- The PipesHub image version is updated (#3636 — @abhishekgml1991).
- Demo stack — a demo-only stack (#3621), Acme stories for sales, support, marketing, finance and HR (#3585), and an AI judge for demo answers in place of phrase lists (#3669, #3822, #3929, #4000, #4009).
- Integration and end-to-end coverage
- Auth, sessions and OAuth (#3643, #3671, #3859, #3860, #4008).
- Permissions and admin routes (#3684, #3689, #3916, #3936).
- Cleanup after every delete path (#3682, #3756, #3792, #3863, #3869, #3927, #3970, #3971).
- AI models, agents, MCP and the agent harness on a real model (#3690, #3693, #3797, #3843, #3844, #3845, #3867, #3871).
- Connector scenario matrix and connector suites (#3644, #3854, #3856, #3857, #3861, #3862, #3864, #3930, #4002).
- Resilience (#3928, #3989, #4001).
- Graph edge schemas (#3793).
- End-to-end UI waits (#3796, #3868, #3870).
- Stale suites, wait-for-demo-data and route-auth updates (#3673 — @uncleSlayer, #3791, #3809, #3824, #3826, #3954).
- Connector delete unit tests (#3821 — @R0gue-one).
- Tests that a later class or function of the same name had replaced now run (#3849, #3850, #3851).
- Docs & cleanup — the README gets the demo video and an install tip (#4033, #4034). Comments and text fragments are cleaned up (#3947 — @darshan-pipeshub, #3958 — @abhishekgml1991).
- Dependencies
- Dependabot bumps across the Node API, frontend, Python services and integration tests: axios, dompurify, electron, undici, multer, markdown-it, katex, sharp, prosemirror-view,
@grpc/grpc-js, engine.io, brace-expansion, source-map-js,@modelcontextprotocol/sdk, sentence-transformers, pyjwt, urllib3, litellm, oauthlib, multidict, pymongo, snowflake-connector-python and fsspec (#3652, #3734, #3735, #3736, #3737, #3738, #3739, #3740, #3741, #3742, #3745, #3746, #3747, #3748, #3749, #3750, #3959, #3960, #3961, #3962, #3963, #3964, #3965, #3967, #3969). @pipeshub-ai/mcpmoves to 3.0.1 (#4037).
- Dependabot bumps across the Node API, frontend, Python services and integration tests: axios, dompurify, electron, undici, multer, markdown-it, katex, sharp, prosemirror-view,
New contributors
- @shrikant-pipeshub made their first contribution in #3919
Full commit-level detail: GitHub release · Compare view