← Back to blog

Weave: three decisions that separate it from a document search

Weave reads documents and makes them answerable in a chat, with the source next to every statement. What the platform does is on the product page. This post describes the three decisions that separate it from an ordinary document search, and points at each step to the document that works the point out. It is a signpost, not a replacement for those documents, and it replaces the earlier version of this post, which still described the v0.2.1 state.

One note before the links: all four documents are in German — the architecture overview, the sequence of a knowledge query, the glossary and the network design. The repository README covers the same ground in English at a lower level of detail.

The current release is v0.7.0: six core services (Ingest, Knowledge, Retrieval, Runtime, Tools, API), two optional CPU-only model services for embeddings and reranking, published container images and one Helm chart for the complete platform, in which every service can run as several replicas. Offline, Weave-Ingest takes documents in and Weave-Knowledge turns them into a searchable chunk store; online, a router decides per request whether anything needs to be searched at all. The overall architecture shows both pipelines in one picture.

1. Processed is not published

A folder someone uploads is not findable by that act. Weave separates processing from publication: the document.processed event only confirms that a job was processed, and the portal response to it reads awaiting_release. Under the regular workflow, Weave-Knowledge indexes document.released and nothing else.

That release is a deliberate act. There is exactly one per job, only the document owner, the knowledge-space owner or an administrator may trigger it, and a quality block refuses it. What it produces is an immutable snapshot. Editing or reprocessing a released job is rejected with HTTP 409; changed content requires a new document version. The export path behaves the same way and always hands out the release snapshot, never a later local state.

The department sees this as a chain of states: released → indexing → available to the AI. The last one appears only once Knowledge has confirmed this exact release together with its snapshot hash, and the text chunks and their embeddings are stored in full. “Processed” never means “indexed”.

Where the release sits in the architecture is in the overall architecture; how the signed event reaches the index worker is in architecture and sequence.

A released document version can be withdrawn from Knowledge after explicit confirmation. The document and its chunks are removed from the search index transactionally, while a tombstone prevents a late release event from indexing the same version again. Import history, the original file and the release snapshot remain for traceability.

2. Permissions act inside the query, not as a filter afterwards

The usual design searches first and then filters out what the asker may not see. That is where systems like this leak. In Weave the database query itself decides what may be read. Teams grant access to knowledge spaces, and before any search the runtime intersects the user’s readable spaces with the bot configuration and any request filter.

Someone outside a knowledge space therefore gets no results: no hidden result, and no error message that reveals one exists. The same holds for agents, whether it is an n8n flow, an MCP client with delegated permissions or a technical identity — more on that below. None of them can widen its own access, and none sees more than the person it is asking for.

The order is the entire point, and it is drawn as a time axis in architecture and sequence: load the bot, resolve permissions, route the intent — and only then does anyone search.

3. No sources, no answer

A guard sits at the end of every request. If the search finds no passages, Weave returns the configured notice with an empty source list. There is no fluent, unsourced answer — and that is precisely the failure mode that makes such systems untrustworthy.

The path there is deliberately short: not every request needs a search, the router determines the kind of question first, and the language model is only consulted after that. Both branches are in the sequence diagram in architecture and sequence.

What has been added since v0.2.1

Agent mode. An LLM bot can configure several subagents instead of its own direct knowledge search, each with its own mission and an explicitly bound knowledge space. The main bot plans the subquestions, the subagents research in parallel within their search budget, the hits are merged and validated against the evidence actually retrieved, and the answer carries the combined sources. Progress streams into the chat live. If a subagent fails, that is named — never silently skipped, never replaced with an invented answer — and if nothing usable comes back at all, the same guard as direct search fires. n8n bots can also answer incrementally, so a single turn may run for hours, held open by keepalive lines across the whole chain.

External access through technical identities. A standalone integration or an external AI agent gets a wti_… token with explicitly granted knowledge spaces — none by default. Rotation, expiry, revocation and an audit log come with it, and access runs exclusively through Weave-Tools, over MCP or REST, under the same scope intersection as a person. Internal retrieval credentials never leave the stack.

Disaster recovery. A passphrase-protected archive exports all Ingest data — users, knowledge spaces, jobs, connections including their credentials, files. Since v0.7.0 it is built from one consistent database snapshot and streamed straight to the browser, with no intermediate file on the server. Restoring it on a fresh installation is performed by the first new administrator, who does not have to be the person who created the backup. The knowledge index then rebuilds itself once an Ingest worker is running; the index maintenance panel in the admin area also lets an administrator trigger the same rebuild manually, recompute every vector, or rebuild the index from the existing releases.

Horizontal scaling (v0.7.0). No service holds state that only it knows. Originals, results and images live in PostgreSQL instead of on a shared file volume, so backend and worker pods share no files; rate limits, runtime settings and migration locks live in the database too. In the cluster, OCR runs in its own worker pool and imports, webhooks and releases in a second one, so nothing waits behind a long scan. Every running job reports in to the database regularly; when a pod disappears, its job is requeued after about two minutes. Scaling runs through an HPA or, as a switch in the Helm chart, through KEDA by queue length. At render time the chart adds up the database connections of all replicas and refuses a deployment that would exceed PostgreSQL’s budget. This chart version is meant for new installations; file storage from earlier versions is not migrated.

SSO sign-in for MCP (v0.7.0). An MCP client such as VS Code can now sign in directly with its user’s own account — through Entra ID or Keycloak, with OAuth and PKCE. Weave-Ingest verifies the access token and maps it to an existing, active user. Nobody is created on the way, and permissions come from the teams in Weave, never from groups in the token.

One honest paragraph on September 22nd. That day, a burst of releases drove so many parallel embedding requests into the embeddings pod that it was OOM-killed, and documents ended up permanently stuck on “indexing failed”. The fix bounds the embeddings service to a fixed number of concurrent requests with a bounded queue, turns embedding failures into scheduled retries that survive a pod restart, and runs the indexing worker with a small, fixed concurrency instead of one process per CPU core.

What an operator should know before going to production

The network is flat. All fifteen containers sit on a single bridge network; there is no second one and no sealed-off segment. What separates the services is not the network but the bearer token each one demands. Those are checked fail-closed, and an unset token yields 503 rather than an open door.

As shipped, the Compose stack publishes eleven host ports. Six of them are meant for users: the portal UI and the Ingest API, which the browser calls directly, the chat interface, the API gateway, the Tools REST endpoint, and — new since v0.2.1 as its own container — the Tools MCP endpoint for external integrations and AI agents. The other five — Knowledge, Retrieval, Runtime and the two model services — are needed by nobody from outside, and are published anyway. Two further points come from the same file: no ports: entry carries a bind address, so all eleven sit on 0.0.0.0, and deploy/ contains neither reverse proxy nor ingress nor certificate handling, so every hop is plain HTTP. TLS and binding to 127.0.0.1 are left to the operator. Only PostgreSQL and Redis have no host port.

Four of the internal credentials are effectively master keys: one resolves the identity behind any personal token, one — new since technical identities — the identity behind any technical token and any SSO access token for MCP, one pulls the complete access map across all knowledge spaces, one retrieves the decrypted provider key of the language model. They deserve handling to match.

The network design shows this topology as a picture, including the ports that are published without anyone outside needing them.

When the vocabulary gets in the way

Chunk, RRF, pgvector, cross-encoder: this post does not quite manage without those words, and the documents manage even less. The glossary explains 82 terms twice — once in general and once in the concrete role they play in Weave — grouped by service.


The source code is public at github.com/bl0rb/Weave, where the README is in English. The wiki is the better entry point for questions about operating the portal, but it is written in German.