# KnowledgeFS Production Deployment KnowledgeFS has one supported production topology: an internal backend service deployed as part of Dify. It has no independent SaaS, private-cloud, or single-host deployment mode. The existing Dify knowledge-base feature and KnowledgeFS intentionally coexist during rollout. This deployment does not migrate, replace, or delete existing Dataset/Document data. ## Dependency ownership | Capability | Owner | KnowledgeFS access | |---|---|---| | Model configuration and credentials | Dify model manager / Plugin Daemon | Dify inner model API | | Datasource configuration, OAuth, and credentials | Dify datasource plugins | Dify inner datasource API | | Physical object storage | Dify `STORAGE_TYPE` implementation | Dify inner storage API | | KnowledgeFS relational state | KnowledgeFS database | `DATABASE_URL` | | Complex document parsing | Unstructured-compatible service | `UNSTRUCTURED_API_URL` | | PDF image rasterization | KnowledgeFS API image | Bundled Poppler `pdftoppm` executable | | Capability signing | Dify | Public JWKS only in KnowledgeFS | KnowledgeFS must never receive model-provider keys, datasource secrets, direct Plugin Daemon credentials, or object-storage provider credentials. ## Dify Compose The canonical service definitions are: - `docker/docker-compose.yaml` - `docker/docker-compose-template.yaml` - `docker/envs/core-services/knowledge-fs.env.example` - `docker/envs/core-services/knowledge-fs-unstructured.env.example` - `docker/envs/core-services/knowledge-fs-unstructured-service.defaults` The Compose service: - starts by default with the rest of Dify; - builds `knowledge-fs/apps/api/Dockerfile` when a prebuilt image is unavailable; - remains on the internal `default` network and exposes only port `8787` to peer services; - receives `DIFY_INNER_API_URL=http://api:5001`; - receives the same inner API key used by Dify's plugin boundary; - waits for the Dify API and its database dependency; - uses `/health` for liveness and `/ready` for traffic readiness. `KNOWLEDGE_INTEGRATED_MODE_ENABLED` controls Workspace provisioning/cutover behavior only. Whether it is `false` or `true`, model, datasource, and object-storage calls always go through Dify. To use the bundled isolated parser, copy `knowledge-fs.env.example` to `knowledge-fs.env` and start Compose with `--profile knowledge-fs-unstructured`. The example points KnowledgeFS at `http://knowledge_fs_unstructured:8000`. An external parser deployment may replace that endpoint and tune the heavy-document limits without changing ordinary Office limits. Run `pnpm dify:compose:config` from `knowledge-fs/` to validate the generated Dify Compose file together with this optional profile from a clean checkout. ## Operator-owned environment The tracked KnowledgeFS environment example intentionally contains only settings that belong to the service: | Variable | Purpose | |---|---| | `DATABASE_URL` | KnowledgeFS PostgreSQL connection string. | | `KNOWLEDGE_DOCUMENT_COMPILATION_RUNTIME` | Durable document worker rollout. | | `KNOWLEDGE_DOCUMENT_MATERIALIZATION_MAX_CONCURRENCY` | Process-wide limit for the complete source materialization phase across all supported formats (source read, parse, media extraction, thumbnail/object writes, and raw/canonical checkpoints). Defaults to `2`, accepts `1..8`, and falls back to the legacy PDF concurrency value when unset. The heavy pre-admission lane is capped at one less than this width (minimum `1`) so ordinary work retains a slot; increase this setting together with the heavy parser limit when intentionally enabling multiple heavy materializations. | | `KNOWLEDGE_DOCUMENT_RETAINED_ARTIFACT_MAX_CONCURRENCY` | Process-wide count limit for canonical parse artifacts retained through outline, semantic, graph, and indexing stages. Defaults to `4` and accepts `1..32`. | | `KNOWLEDGE_DOCUMENT_RETAINED_ARTIFACT_MAX_BYTES` | Aggregate conservative JS-heap charge for retained canonical parse artifacts. Defaults to `134217728` (128 MiB), accepts 1 MiB through 1 GiB, and lets one artifact at or above the budget run exclusively rather than deadlocking. | | `KNOWLEDGE_PDF_RASTERIZER` | PDF image rasterizer. The production image defaults to `poppler`; set `off` as a kill switch. | | `KNOWLEDGE_PDF_RASTERIZER_DPI` | Main PDF image resolution; the bounded deployment default is `144`. | | `KNOWLEDGE_PDF_RASTERIZER_THUMBNAIL_DPI` | Thumbnail resolution; the bounded deployment default is `48`. | | `KNOWLEDGE_PDF_RASTERIZER_TIMEOUT_MS` | Poppler subprocess timeout; the deployment default is `30000`. | | `KNOWLEDGE_PDF_RASTERIZER_MAX_ASSETS` | Maximum PDF assets rasterized for one document; the deployment default is `500`. | | `KNOWLEDGE_PDF_RASTERIZER_MAX_CONCURRENCY` | Maximum concurrent Poppler page batches per API replica; defaults to `2` and accepts `1..8`. | | `KNOWLEDGE_VISUAL_EMBEDDING_MAX_ASSET_BYTES` | Per-image raw-byte ceiling for profile-driven visual embedding; defaults to 20 MiB. | | `KNOWLEDGE_VISUAL_EMBEDDING_MAX_BATCH_ASSETS` | Maximum images in one physical embedding request; defaults to `8`. | | `KNOWLEDGE_VISUAL_EMBEDDING_MAX_BATCH_BYTES` | Aggregate raw image bytes in one physical embedding request; defaults to 32 MiB. | | `KNOWLEDGE_VISUAL_EMBEDDING_MAX_CONCURRENCY` | Process-wide visual embedding lifecycle concurrency; defaults to `2` and accepts `1..8`. | | `KNOWLEDGE_MULTIMODAL_ENRICHMENT_MAX_CONCURRENCY` | Process-wide reasoning-model image-understanding concurrency; defaults to `4`. | | `KNOWLEDGE_MULTIMODAL_ENRICHMENT_MAX_IMAGE_BYTES` | Per-image raw-byte ceiling for document image understanding; defaults to 10 MiB. | | `KNOWLEDGE_MULTIMODAL_ANSWER_MAX_CONCURRENCY` | Process-wide lifecycle concurrency for loading answer images and calling the active vision reasoning model; defaults to `2`. Calls also share the global model-request gate. | | `KNOWLEDGE_MULTIMODAL_ANSWER_MAX_IMAGE_ATTACHMENTS` | Maximum query/evidence image blocks in one answer request; defaults to `8`. | | `KNOWLEDGE_MULTIMODAL_ANSWER_MAX_IMAGE_BYTES` | Per-image raw-byte ceiling for answer generation; defaults to 10 MiB. | | `KNOWLEDGE_MULTIMODAL_ANSWER_MAX_TOTAL_IMAGE_BYTES` | Aggregate raw image-byte ceiling for one answer request; defaults to 32 MiB and must be at least the per-image ceiling. | | `KNOWLEDGE_FS_CAPABILITY_V2_ENABLED` | Capability-v2 verifier rollout. | | `KNOWLEDGE_FS_CAPABILITY_V2_PUBLIC_JWKS` | Public verification key set issued by Dify. | | `KNOWLEDGE_QUERY_IMAGE_EXPANSION_TIMEOUT_MS` | Timeout for the single bounded vision expansion used by Deep/Research or image-only Fast fallback; defaults to 8000 ms. | | `KNOWLEDGE_RESEARCH_REASONING_MAX_OUTPUT_TOKENS` | Structured planner/judge output ceiling; defaults to `8192` so hidden reasoning tokens do not force a second model call. | | `KNOWLEDGE_RESEARCH_REASONING_TIMEOUT_MS` | Per-call Research planner/judge deadline; defaults to `60000`. Caller cancellation and the request-wide Research deadline remain authoritative. | | `KNOWLEDGE_RESEARCH_MAX_RERANK_CANDIDATES` | Initial cross-encoder pool shared fairly across the original query and up to three planned intents. Defaults to and is capped at `200`; lower it to trade multi-intent recall depth for latency/cost. A durable evidence judge may add one separately plan-bounded supplemental list. The response reports the initial pool and every selected list count, so total provider work remains observable. | | `KNOWLEDGE_DIRECT_UPLOAD_SMALL_FALLBACK_MAX_CONCURRENCY` | Process-wide active-request limit for the API-buffered upload compatibility path. Defaults to `2` and accepts `1..8`. | | `KNOWLEDGE_DIRECT_UPLOAD_SMALL_FALLBACK_MAX_RESERVED_BYTES` | Aggregate source-byte reservation for admitted buffered uploads. Defaults to `31457280` (30 MiB), must be at least the configured per-file fallback limit, and is capped at 100 MiB. | | `KNOWLEDGE_BUFFERED_DOCUMENT_UPLOAD_MAX_CONCURRENCY` | Process-wide active-request limit for legacy/capability multipart document routes, acquired before Hono form validation. Defaults to `2` and accepts `1..8`. | | `KNOWLEDGE_BUFFERED_DOCUMENT_UPLOAD_MAX_RESERVED_BYTES` | Aggregate conservative retained-buffer charge for direct multipart uploads. Defaults to `201326592` (192 MiB). Each request reserves three times its bounded multipart envelope to cover the stream chunks, Hono body/FormData caches, and the handler `File.arrayBuffer` copy; this is an admission estimate, not an RSS guarantee. | | `KNOWLEDGE_BUFFERED_DOCUMENT_UPLOAD_IDLE_TIMEOUT_MS` | Maximum continuous idle interval while reading a direct multipart or small-file fallback body. Defaults to `30000`; expiry cancels the reader, returns 408, and releases admission. | | `KNOWLEDGE_BUFFERED_DOCUMENT_UPLOAD_TOTAL_TIMEOUT_MS` | Generous total body-read deadline for a direct multipart or small-file fallback request. Defaults to `600000`, must be at least the idle timeout, and returns 408 on expiry. | | `UNSTRUCTURED_API_URL` | Parser endpoint for complex formats. | | `UNSTRUCTURED_API_KEY` | Optional parser authentication. | | `UNSTRUCTURED_MAX_CONCURRENCY` | Process-wide limit shared by every remote parser request; defaults to `2`. | | `UNSTRUCTURED_HEAVY_MAX_CONCURRENCY` | Nested limit for every PDF and structurally/byte-heavy Office, email, EPUB, ODT, or RTF request. The bundled parser profile uses `1`; it must not exceed `UNSTRUCTURED_MAX_CONCURRENCY`. The materialization pre-admission lane follows this value but is capped at `KNOWLEDGE_DOCUMENT_MATERIALIZATION_MAX_CONCURRENCY - 1` (minimum `1`) to preserve ordinary-document progress. `UNSTRUCTURED_PDF_MAX_CONCURRENCY` remains a lower-precedence compatibility alias. | | `UNSTRUCTURED_MAX_INPUT_BYTES` | Maximum body admitted by the remote parser client. Defaults to `15728640` (15 MiB), matching the product upload default, and is capped at 50 MiB. Native parsing keeps its separate 10 MiB routing threshold. | | `UNSTRUCTURED_REQUEST_TIMEOUT_MS` | Total timeout for an ordinary parser request and response body; defaults to `600000` (10 minutes) and accepts up to `3600000`. | | `UNSTRUCTURED_HEAVY_REQUEST_TIMEOUT_MS` | Heavy-document total timeout. The bundled page-parallel profile uses `2400000`, below the `3600000` validation ceiling. `UNSTRUCTURED_PDF_REQUEST_TIMEOUT_MS` remains a lower-precedence compatibility alias. | | `UNSTRUCTURED_MAX_RESPONSE_BYTES` | Maximum parser response body; defaults to `33554432` (32 MiB). | | `UNSTRUCTURED_MAX_RETRIES` | In-process retry count for explicit retryable HTTP responses. Ambiguous transport failures are never retried inline. The integrated deployment uses `0`; durable compilation owns whole-attempt retries. | Research planner/judge prompts treat retrieved document text as untrusted data and prohibit obeying embedded instructions. They use strict structured output, expose no tools, and may only issue one normalized supplemental query inside the same tenant, permission snapshot, resource budget, and request deadline. This limits prompt-injection impact to retrieval quality and one bounded model / retrieval round; it is not a substitute for document trust and model-call monitoring. Compose injects `DIFY_INNER_API_URL` and `DIFY_INNER_API_KEY`; do not duplicate them in the operator-owned env file. Do not add `MINIO_*`, cloud object-storage credentials, provider API keys, `PLUGIN_DAEMON_*`, datasource tokens, or OAuth client secrets. `DIFY_OBJECT_STORAGE_REQUEST_TIMEOUT_MS` bounds each authenticated inner object-storage request, including response consumption. It defaults to `60000`; transport failures and 408/409/425/429/5xx responses remain retryable at the durable compilation layer. The optional `knowledge-fs-unstructured` Compose profile starts an isolated parser service with six pages per child request, three child workers, and zero child retries. It does not modify Dify's existing `unstructured` service or legacy ETL traffic. The tracked `knowledge-fs-unstructured-service.defaults` file contains only service-side page-parallel values; operator-owned `knowledge-fs-unstructured.env` is loaded afterwards and can override it. The required file deliberately uses a non-`.env` suffix so it remains tracked in clean checkouts. The `.env.example` files remain copy-only templates and are never loaded as runtime configuration. Compose pins the isolated multi-architecture image to the digest used by the output-equivalence and resource benchmarks; review the same contracts before intentionally changing that digest. Child requests are at or below the split size and therefore partition locally instead of recursively spawning more requests. The KnowledgeFS client keeps a process-wide limit of `2` and adds a heavy-workload nested limit of `1`. Every PDF remains heavy because compressed PDF object streams make a bounded page-count scan unreliable. ZIP-backed remote formats become heavy when their admitted body exceeds 8 MiB or a bounded central-directory inspection identifies a large container (for example, many slides, sheets, entries, or more than 64 MiB of declared expanded content). Opaque legacy Office, RTF, and mail containers use the heavy lane conservatively; remote inputs that remain standard receive the 600-second deadline. Declared ZIP64, multi-disk, excessive-entry, or excessive-expansion containers are rejected before provider work; the classifier never inflates entries and does not change parser output. Heavy requests receive the 2,400-second deadline. The isolated parser is capped at four CPUs and 6 GiB in the reference Compose deployment. Those limits bound host impact, but the upstream synchronous API has no cancellation or idempotency contract: a timed-out connection can leave child work running. Unstructured transport timeouts are therefore terminal for automatic durable retries; inspect the parser and retry manually after it is idle. Multi-replica deployments need a shared admission layer because each incoming request creates its own child thread pool. The Kubernetes baseline does not own an Unstructured deployment and retains generic client limits. Operators with a different resource envelope must benchmark representative narrative, table, and scanned pages before changing either concurrency limit. ## PDF image rasterization The production API image installs Poppler and verifies `pdftoppm` during the image build. Its image defaults enable rasterization at 144 DPI, generate 48 DPI thumbnails, stop an individual Poppler operation after 30 seconds, cap one document at 500 rasterized assets, and run at most two Poppler page batches concurrently per API replica. The process still runs as the unprivileged `node` user. The Dify Compose service keeps canonical values in `docker/envs/core-services/knowledge-fs.env`, where they override the image defaults. It maps only whitelisted `DIFY_ROOT_*_OVERRIDE` proxies, so an explicitly set PDF rasterizer value in `docker/.env` takes precedence without exposing the rest of the root environment. An unset or empty root value leaves the service env (or image default when that file is absent) in control. Set `KNOWLEDGE_PDF_RASTERIZER=off` in either operator env during an incident or on a deliberately constrained deployment. Do not set `KNOWLEDGE_PDF_RASTERIZER_COMMAND` for the published image; its bundled command is on `PATH`. Rasterization supplies durable image objects for PDF image elements when the parser returns layout coordinates without image bytes. It does not repair already-published parse artifacts. Re-run the document ingestion after deploying the corrected image to repopulate images that were previously stored without an asset reference. ## Canonical artifact retention bounds The compilation claim batch may contain more documents than the source materialization limit. A canonical `ParseArtifact` also remains live after parsing while outline generation, semantic chunking, embedding, graph extraction, and index projection consume it. A process-wide FIFO admission therefore bounds this later lifetime independently by both artifact count and a conservative retained-byte estimate. The estimator walks the existing object graph in place; it does not call `JSON.stringify` or copy large strings. It charges strings as UTF-16 plus array/object/property overhead and stops once the configured byte ceiling is reached. This is an admission estimate, not an exact V8 heap measurement. An artifact charged at the full ceiling waits until it can run alone, so an operator may lower the aggregate budget without making a previously accepted large document impossible. Fresh and resumed compilations use the same gate. The materialization slot is handed off only after the retained-artifact lease is acquired, which prevents completed artifacts from accumulating in an unbounded memory queue. Downstream work never reacquires materialization, so the fixed lock order does not introduce a gate cycle. Cancellation while queued never enters outline/index work, and every success or failure releases the lease. ## Visual embedding memory bounds Image-byte visual embedding is activated by the immutable embedding capability snapshot of each knowledge space, not by a deployment-selected model. A profile that declares image input is validated with a real text probe plus 1x1 PNG document and query probes before activation; every probe must return the same model identity and vector dimension. Text-only profiles skip visual projection work. Image-byte visual embedding is microbatched independently of the 128-node projection batch. The default request limits are eight assets and 32 MiB of raw image bytes (`KNOWLEDGE_VISUAL_EMBEDDING_MAX_BATCH_ASSETS=8` and `KNOWLEDGE_VISUAL_EMBEDDING_MAX_BATCH_BYTES=33554432`). The existing `KNOWLEDGE_VISUAL_EMBEDDING_MAX_ASSET_BYTES` remains the per-image limit and must not exceed the batch byte limit. One process-wide visual lifecycle gate is shared by all document compilations in the API process. `KNOWLEDGE_VISUAL_EMBEDDING_MAX_CONCURRENCY` defaults to `2` and accepts values from `1` through `8`. A queued compilation acquires this gate before it reads any image object and holds the slot through every microbatch request. This bounds cross-document raw-image and base64 amplification; queue cancellation is honored before object reads begin. Image bodies use `getObjectStream`; visual embedding does not add one serial `HEAD` round trip per image and does not call the adapter's broader buffered `getObject` path. The consumer accepts no more than the configured number of source bytes, cancels the stream as soon as the next chunk would cross that limit, and creates no contiguous image body larger than the limit. The production Dify adapter forwards response chunks through its stream and does not first accumulate and concatenate the complete object. For a valid multi-chunk image, the final bounded concatenate can temporarily hold the accepted chunks and one equally bounded destination body. Conservative preflush bounds that raw assembly peak by `MAX_BATCH_BYTES + MAX_ASSET_BYTES` per active lifecycle (52 MiB with the defaults); the lifecycle gate limits simultaneous peaks across documents. Base64/JSON transport representations require additional bounded headroom, so `MAX_BATCH_BYTES` is not an RSS limit. Before the next body read, the adapter also flushes the current batch whenever its remaining byte budget cannot hold one maximum-sized image. The provider still creates a transient base64 representation for Dify model-runtime transport, so the byte limit is a raw-image budget rather than a whole-process heap ceiling. Keep the existing global model-request concurrency limit enabled when increasing it. ## Image-query rollout The public Dify API accepts query images as actor-owned Dify `UploadFile` references. KnowledgeFS does not receive storage credentials or persist a second copy of the bytes: Dify validates tenant and account ownership, MIME, size, count, and aggregate size, then KnowledgeFS resolves each file through the authenticated inner API for the lifetime of one query run. There is no separate visual model or image-retrieval feature flag. The active published embedding profile is authoritative: - if its frozen capability supports image input, document images and query images are embedded by that exact profile model and searched in the matching visual vector space; - an embedding-profile migration rebuilds visual projections with the candidate model, or removes stale visual projections when the candidate is text-only; - a legacy capability snapshot is granted image input only when the current catalog identity and full frozen digest still match. Catalog drift or outage fails closed to text. The active published reasoning profile independently controls image understanding. When its frozen capability supports image input, the same reasoning LLM handles bounded query-image expansion, document image enrichment, and final answers with query/retrieved image blocks. A text-only LLM receives only OCR, caption, and ordinary text evidence; image bytes are never sent to it. Fast uses direct visual retrieval without a reasoning call when the embedding model supports image input. For an image-only Fast request with a text-only embedding profile, a vision-capable reasoning model may perform one bounded text expansion; a pure-image request is rejected explicitly when neither selected model supports images. Deep and Research perform at most one bounded image-to-text expansion when useful. Durable Research persists the derived text so retry/replay does not repeat that call. Research model calls remain in dry-run estimates and durable budget accounting. The production assembly no longer reads `KNOWLEDGE_VISUAL_EMBEDDING_PROVIDER`, `KNOWLEDGE_VISUAL_EMBEDDING_MODEL`, `KNOWLEDGE_VISUAL_EMBEDDING_QUERY_MODE`, `KNOWLEDGE_QUERY_IMAGE_RETRIEVAL_ENABLED`, `KNOWLEDGE_MULTIMODAL_ENRICHMENT_PROVIDER`, or `KNOWLEDGE_MULTIMODAL_ANSWER_PROVIDER`. Only byte, count, concurrency, timeout, and image-detail settings remain operator-owned. Publications activated through the current profile-migration runtime receive the correct visual rebuild automatically. A vision profile that was already active before this release must run one ordinary profile reindex/migration (or recompile its documents) to backfill visual projections; the query path never mixes a new profile query vector with an old visual vector space. The request bounds are four images, 10 MiB per image, 32 MiB in aggregate, with MIME restricted to PNG, JPEG, WebP, and GIF. Operational traces and terminal metadata use these stable degradation reasons: `query-image-visual-leg-unavailable`, `query-image-ignored-no-vision-model`, and `query-image-expansion-timeout`. ## Database release Apply checked-in KnowledgeFS migrations through the controlled migration runner before scaling a new binary: ```bash pnpm db:migrations:check pnpm local:db:migrate ``` Use the environment's normal migration job in production rather than running the local command from an application container. The KnowledgeFS migration runner owns only KnowledgeFS tables. It must not mutate existing Dify Dataset/Document tables or perform a production data migration. Keep destructive legacy-removal flags disabled until the separately approved zero-traffic, backup/restore, DBA, and CAB gates are complete. ## Readiness contract Production `/ready` fails closed unless all enabled capabilities are assembled. The base checks include: - an authentication verifier; - Dify model-runtime configuration; - Dify datasource-runtime configuration; - Dify object-storage configuration; - durable database repositories required by enabled workers and product routes. `/health` is liveness and component diagnostics; it is not permission to receive production traffic. A service with `/health=200` and `/ready=503` must remain out of rotation. Provider-direct presign and multipart upload remain unavailable because the Dify storage bridge deliberately does not expose provider-specific primitives. The upload-session compatibility path therefore streams each request into one bounded API buffer and writes it through the Dify inner storage API. One process-shared admission gate is acquired before that buffer is allocated and held until the object write finishes. It limits both active requests (default `2`) and aggregate retained source bytes (default 30 MiB); a queued request that is cancelled never allocates its upload buffer. The 15 MiB per-file product limit is unchanged. These limits are per API replica, so operators must include replica count when sizing the deployment-wide memory envelope. ## Release validation Before publishing an image: ```bash pnpm install --frozen-lockfile pnpm typecheck pnpm test pnpm lint:backend pnpm openapi:export:test pnpm db:migrations:check pnpm dify:compose:config pnpm docker:api:build pnpm docker:api:bundle-smoke git diff --check ``` The isolated bundle smoke is not a production dependency test. In a Dify-connected environment, also verify: 1. `/health` and `/ready`. 2. A tenant-scoped KnowledgeSpace create/read. 3. A bounded document upload and object read through Dify storage. 4. Embedding, rerank, LLM, and model-catalog calls through Dify model instances. 5. Datasource validation/browse through a Dify-managed `credentialId`. 6. No model, datasource, OAuth, Plugin Daemon, or storage credentials appear in KnowledgeFS environment variables, requests, logs, or database rows. 7. Existing Dify knowledge-base flows remain unchanged. ## Workspace rollout Roll out Workspace by Workspace. Keep the integrated-mode/capability flags disabled by default, then enable only after the selected Workspace has: - durable KnowledgeFS provisioning state; - capability verification; - successful model, datasource, and storage smoke; - rollback evidence and monitoring ownership. The rollout flag changes admission and provisioning behavior. It does not switch transports and does not authorize a fallback runtime. ## Kubernetes `infra/kubernetes/dify-integration-baseline.yaml` is an inert reference with zero replicas, an internal `ClusterIP`, fail-closed probes, and no public ingress. A downstream Dify deployment may adopt it only while preserving the same ownership boundaries. ## Rollback Rollback the KnowledgeFS image or disable the affected Workspace cutover. Preserve KnowledgeFS database rows and Dify-owned objects unless a reviewed recovery procedure says otherwise. Do not rotate the Dify inner key, delete existing knowledge-base data, or redirect KnowledgeFS to a direct storage/plugin endpoint as a rollback shortcut. After rollback, rerun Dify-connected health and tenant smoke, confirm the existing knowledge-base feature is unaffected, and record the release and rollback evidence.