# Knowledge Bases & Workspace Context

> Applies to: ChengOS v0.1.0+ ｜ Last verified: 2026-08-12 ｜ Source: `crates/cheng-core/src/domain/knowledge_base`, `crates/cheng-core/src/domain/workspace`, `crates/cheng-nodes/src/nodes/builtin/workspace`, `crates/cheng-engine/src/executor/context.rs`

A **workspace** is the container everything else lives in — documents, tables, conversations,
files, and knowledge bases. A **knowledge base** is a workspace-scoped vector collection: the
searchable half of a workspace.

Understanding how the active workspace reaches a running node is what makes the rest of this
page (and most of the data nodes) make sense, so start there.

## Workspaces

```text
Workspace (aggregate root)
  ├── tenant_id, owner_id
  ├── WorkspaceMember[]   (owner / admin / editor / viewer)
  └── WorkspaceSettings
        ├── default_workflow_id     (workflow used for AI conversations)
        ├── features                (per-workspace feature flags)
        ├── storage_quota
        ├── trace_retention
        └── artifacts               (upload limits, LLM preview budget, retention)
```

**Member roles** are `owner`, `admin`, `editor`, `viewer`. Only `owner` and `admin` can change
workspace settings.

Workspaces are soft-deleted (`deleted_at`), so removing one does not immediately destroy the
documents and tables inside it.

`trace_retention` and `artifacts` are worth knowing about because they are the per-workspace
overrides for two things people usually look for in global config: how long execution traces are
kept, and how large an artifact may be before the store rejects it.

## Ambient context: how a node knows its workspace

Most data nodes have a `workspace_id` input that is **hidden on the canvas** (`x-hidden`) and
normally left blank. The engine injects it at runtime.

Three fields are treated this way — `AMBIENT_CONTEXT_FIELDS`:

```text
conversation_id   workspace_id   document_id
```

They are resolved from the agent context's metadata first, then from execution metadata
(`conversation_id` additionally falls back to the agent's `session_id`).

The security-relevant detail: **for tool calls, ambient injection force-overrides whatever the
model supplied.** An LLM cannot widen its own scope by inventing a `workspace_id` in a tool
call, because the engine replaces the field after the model produces it. This is the same
principle as the sandbox root, where a model-supplied `sandbox_root` is dropped outright in
favour of the static canvas value.

`workspace/context` exposes the injected value as a normal node output — `workspace_id` plus a
`has_workspace` boolean — for when a workflow needs to branch on it or log it. `has_workspace`
is false when the workflow was triggered outside any workspace, which is a real case for
channel and API entry points.

Cross-workspace overrides are deliberately **not** available as canvas nodes. App and API entry
points own workspace selection; a workflow cannot reach sideways into another workspace.

## Workspace bootstrap

When a workspace record is created, a bootstrap service optionally populates it. Every option
defaults to **off**; callers opt in:

| Option | Creates |
|---|---|
| `create_folders` | The root folder tree (documents / tables / conversations / workflows) |
| `create_welcome_document` | A welcome document (requires `create_folders`) |
| `create_default_conversation` | A starter conversation (requires `create_folders`) |
| `create_default_table` | A starter table (requires `create_folders`) |
| `create_knowledge_base` | A default knowledge base |
| `configure_ai` | Feature flags and `default_workflow_id` |

The dependency rules are enforced by `normalize()`, so an inconsistent combination is corrected
rather than silently half-applied. The `default_full()` preset creates **only the folder tree** —
documents, tables, conversations, and knowledge bases are created lazily by their own nodes when
a user actually needs them.

## Knowledge bases

```text
KnowledgeBase
  ├── workspace_id
  ├── name, description
  ├── collection_name      ← the Qdrant collection
  ├── status               ← active | building | error | archived
  ├── document_count
  └── config { embedding_model, chunk_size, chunk_overlap, ... }
```

The collection name is generated as `kb_<workspace_id>_<kb_id>` unless you supply one. That
naming is the isolation boundary: two workspaces can never collide in the vector store, because
the workspace id is part of the collection name.

**Status** matters when you script against it. A knowledge base is `building` while it is being
created or indexed, and querying it then is premature; `error` means indexing failed;
`archived` means disabled but retained.

`config` carries the embedding parameters — `embedding_model`, `chunk_size`, `chunk_overlap` —
plus arbitrary extra settings. These are per-knowledge-base, not global: two knowledge bases in
one workspace can use different embedding models. That is also why they are not
interchangeable — vectors written with one embedding model cannot be searched with another.

## The knowledge base node

`workspace/knowledge_base` has three modes:

| Mode | Requires | Returns |
|---|---|---|
| `select` (default) | `kb_id` | The chosen knowledge base |
| `list` | — | Every knowledge base in the workspace |
| `create` | `name` (optional `description`, `embedding_model`) | The new knowledge base |

The output exposes `workspace_id` and `kb_id` as **flat top-level fields** in addition to the
structured `knowledge_base` object, precisely so they can be wired straight into a RAG node's
input without an intermediate mapping step.

In `select` mode, `kb_id` is a dynamic dropdown populated from
`GET /api/v1/workspaces/current/knowledge-bases?limit=200` — you pick by name, the node stores
the UUID.

## REST access

Knowledge bases are managed under a workspace:

```text
GET|POST      /workspaces/:workspace_id/knowledge-bases
GET|PUT|DELETE /workspaces/:workspace_id/knowledge-bases/:kb_id
GET           /workspaces/current/knowledge-bases
```

The `current` route resolves the workspace from the request context, which is what the node's
dropdown uses.

## Putting it together

The usual arrangement is:

```text
workspace/knowledge_base (select)
        │ kb_id
        ▼
rag/document_indexer   ──►  writes vectors into the collection
        …
rag/retriever          ──►  reads them back at query time
```

Create the knowledge base once, then reference it by id from both the indexing workflow and the
answering workflow. [RAG Pipeline Design](/docs/data-rag-pipeline) covers what happens inside
those two nodes.

## Next steps

- [RAG Pipeline Design](/docs/data-rag-pipeline) — indexing and retrieval on top of a knowledge base.
- [Document Workspace](/docs/data-document-workspace) — the documents that get indexed.
- [Storage, Redis & Vector Configuration](/docs/config-storage-redis) — configuring the vector store itself.
