Curiosity

Architecture

Curiosity Workspace is a single deployable that brings together three layers that usually require separate systems:

  • Graph layer — a typed, schema-driven knowledge graph (nodes, edges, properties, traversals).
  • Search layer — text, vector, and hybrid retrieval with property and graph-relationship facets.
  • AI layer — embeddings, NLP enrichment, LLM orchestration, AI tools, and agents.

The unified runtime is the point: graph relationships improve search filtering, search results ground AI answers, and permissions (ReBAC) are enforced consistently across all three.

Logical architecture

flowchart LR Clients["Clients<br/>(built-in UI, custom interfaces, external APIs)"] Gateway["API & UI Gateway<br/>(Kestrel, controllers, JWT auth)"] subgraph Workspace["Curiosity Workspace process"] Endpoints["Custom Endpoints<br/>+ AI Tools"] Graph["Graph Engine<br/>(in-memory, ACID writes,<br/>lock-free reads)"] Search["Search Engine<br/>(text + vector,<br/>ACL-aware)"] AI["AI Layer<br/>(embeddings, NLP, LLM)"] Parsers["Parsers / Linkers<br/>(file parsing, entity linking)"] Tasks["Scheduled Tasks"] end Storage[("Persistent storage<br/>(MSK_GRAPH_STORAGE)")] Providers["LLM / Embedding<br/>providers"] Clients --> Gateway Gateway --> Endpoints Endpoints --> Graph Endpoints --> Search Endpoints --> AI Graph --> Storage Search --> Storage Parsers --> Graph Tasks --> Endpoints AI <--> Providers Search <--> AI

Components

Component Purpose
API & UI Gateway Kestrel-hosted HTTP server. Serves the built-in front-end and routes programmatic requests. JWT authentication and ReBAC are enforced here.
Graph Engine In-memory, purpose-built engine for low-latency traversals and high-throughput updates. ACID writes, lock-free reads.
Search Engine Text and vector indexes built from explicit field configuration. ACLs are indexed alongside content and applied at query time.
Parsers Background processes that extract text and metadata from incoming files (OCR, STT, format-specific parsers). Sandboxed via Landlock on Linux.
Linkers Background processes that materialize edges from parsed content (entity linking, deduplication, similarity edges).
Custom Endpoints / AI Tools C# code that runs inside the workspace, with full access to the graph, search, and AI runtime.
Scheduled Tasks Cron-driven background work for ingestion, reindexing, enrichment, and analytics.

Deployment model

  • Single-container delivery (curiosityai/curiosity). The Workspace is a single Kestrel-hosted process with internal threading rather than a fleet of microservices — components are reached as in-process calls.
  • Out-of-process sandbox for file parsing only (Landlock on Linux), so a malicious document can't compromise the workspace itself.
  • Persistent storage at MSK_GRAPH_STORAGE. Backing storage is platform-specific (EBS, Azure Disk, Persistent Disk, vSphere, local SSD).
  • Vertical scale first — the graph engine is memory-resident; more RAM and faster disk are the primary scaling levers. Horizontal scaling (read replicas) is on the roadmap for very-large deployments.

See Storage and indexing for the detailed layout and Scaling for capacity planning.

Request flows

Search and discovery

sequenceDiagram participant U as User participant FE as Front-end participant GW as Gateway participant S as Search Engine participant G as Graph Engine U->>FE: types query FE->>GW: POST /api/cce/search (Bearer JWT) GW->>GW: validate JWT, resolve user + teams GW->>S: SearchRequest + user's ACL filter S->>S: text + vector retrieval S->>G: hydrate nodes / neighbors G-->>S: nodes S-->>GW: ranked, ACL-filtered results GW-->>FE: JSON

AI-assisted answer (RAG)

sequenceDiagram participant U as User participant Chat as Chat view participant GW as Gateway participant Tools as AI tool runtime participant S as Search Engine participant LLM as LLM provider U->>Chat: asks a question Chat->>GW: chat turn GW->>Tools: invoke chat with available tools Tools->>LLM: prompt + tool definitions LLM-->>Tools: tool call (e.g., FindSimilarTickets) Tools->>S: CreateSearchAsUserAsync(req, CurrentUser) S-->>Tools: ACL-filtered nodes Tools->>Tools: AddSnippet(uid, text) for citations Tools->>LLM: tool result LLM-->>Tools: answer with [snippet] refs Tools-->>GW: answer + tool-call trace GW-->>Chat: rendered answer with citations

Ingestion

sequenceDiagram participant C as Connector participant GW as Gateway participant G as Graph Engine participant P as Parsers/Linkers participant S as Search Engine C->>GW: API token (ingestion scope) C->>G: TryAdd(Node), Link(...) C->>G: CommitPendingAsync() G->>P: notify new content P->>G: parsed text + extracted entities P->>G: add MentionsEntity edges G->>S: index changed fields (text + vector) S-->>S: new content searchable

ACID and concurrency

  • Writes are ACID: a CommitPendingAsync() either applies in full or not at all. Two writers committing the same logical entity concurrently get one winner; the other gets a commit_aborted error and can retry.
  • Reads are lock-free: heavy query traffic and ingestion coexist without blocking each other.
  • Index updates are eventually consistent with respect to writes — there's a small window where a freshly committed node is in the graph but not yet in the search index. Application code that requires read-your-writes should query the graph directly, not the search engine, during that window.

Security model (ReBAC)

  • Access is determined by paths in the graph: user → team → owns → resource.
  • ACLs are ingested by the connector via RestrictAccessToTeam, RestrictAccessToUser, MarkFileAsPrivate.
  • At query time, the search engine compiles the user's team memberships into an ACL filter and applies it before ranking.
  • The graph engine enforces the same check on direct fetches.

See Access Control Model for the full deep dive and Permission model architecture for the data flow.

Failure modes worth knowing

Failure Impact Mitigation
LLM provider unreachable Chat and RAG tools fail; search/graph keep working Configure a fallback provider; degrade gracefully in tool code
Embedding provider unreachable New nodes don't get vector entries; pure text retrieval still works Vector queries return text fallbacks; rebuild after recovery
Storage full Writes fail; reads continue from in-memory state until restart Alert on disk usage > 80% and expand the PVC
OOM during embedding rebuild Container restart loop Scale up RAM, or stage the rebuild during low traffic
MSK_GRAPH_MASTER_KEY change Encrypted properties unreadable Treat the key as the most important secret you back up

Design goals

  • Schema-first clarity — you control which types exist and how they relate.
  • Configurable retrieval — tune relevance without rewriting your app.
  • Safe extensibility — move business logic into versionable endpoints and controlled interfaces.
  • Operational control — every deployment can be monitored, secured, and promoted across environments.

Next steps

© 2026 Curiosity. All rights reserved.
Powered by Neko