Design: Doso Architecture

Applied architecture combining workflow-graph engineering and the knowledge-graph pipeline into one instance (API + database) with thin surfaces.

Goals#

  • Ingest personal sources (notes, bookmarks, transcripts, emails exports, articles).
  • Build a durable, provenance-rich knowledge graph.
  • Answer multi-hop questions with cited edges.
  • Run optional multi-agent “diamonds” for research, audit, and discovery over that graph.
  • Start simple; scale storage without redesigning the pipeline.

Non-goals#

  • Replacing a general chat product UI.
  • Fully autonomous unsupervised write-back to production facts.
  • Training custom NER models.
  • Real-time collaborative multi-user editing.

Logical components#

1. Source connectors#

Normalize everything to:

1Document { id, title, text, uri?, collected_at, mime?, checksum, meta }

Implemented:

ConnectorHowNotes
Library uploadUI / POST /v1/content/documentstext, md, pdf, email shorthand, messages[]
Host CLI ingestpaired doso-runtime or doso-sync POSTing documentsSee Connections — CLIs stay on the host
Live askFirecrawl + xAI searchIngested mid-ask with stable URIs; see Ask, chat, and live sources

URI dedupe + GET …/documents/by-uri keep sync and live ingest idempotent.

2. Staging store#

  • Append-only document blobs + metadata.
  • Candidate extractions (pre-canonical) keyed by doc_id + extractor_version.
  • Run ledger: workflow id, status, token/cost counters, caps.

3. Graph store#

The graph is the canonical knowledge model used by Ask. Library documents remain provenance; Memories and entity wiki pages are projections over verified relations rather than independent fact stores. The entity wiki is compiled at read time from an entity’s verified one-hop neighborhood and may reuse a cached summary only while its relation fingerprint still matches. See Entity wiki and statements.

Minimum viable schema:

Nodes entities(id, canonical_name, type, description, aliases jsonb, mention_count, props jsonb)
Edges relations(id, src_id, predicate, dst_id, source_doc_id, chunk_id?, confidence?, extracted_at, status)
status: candidate | verified | rejected

When an embedding provider is configured, entity and document-chunk vectors support two jobs only: retrieve library passages for Ask, and bind an extracted name to an existing same-type entity when cosine similarity and token overlap agree they are the same referent. Embeddings are not a second memory.

v1 recommendation: SQLite (or Postgres) adjacency tables. Optional NetworkX projection for local algorithms/visualization. Neo4j later if Cypher/path UX becomes primary.

4. Workflow runners (implemented: crates/doso-core wgraph + workflows + skills)#

Rust workflow-graph runtime — coordination is code, not another chat turn:

  • run_agent / run_code nodes with NodeRole + contracts
  • parallel barrier with null-on-failure + agent semaphore
  • pipeline (streaming map), diamond, loop_until_dry
  • route for deterministic branches; structured topology_dag on traces
  • Caps: max agents/nodes/duration/rounds
  • Named catalog in workflows (POST /v1/workflows/run)

Persisted, user-authored definitions use the declarative scheduler in skills/engine.rs through skills/interpreter.rs. It adds dependency-ready parallel execution, all/any/quorum joins, runtime contracts, bounded dynamic maps and loops, nested subgraphs, verifier voting, safe-result caching, resource locks, and policy-derived approval/isolation. These database-backed DAGs are distinct from the fixed Rust catalog below.

Named topologies:

WorkflowShape
Ingestchunk → parallel(extract) → barrier merge → resolve → assemble(candidates)
Askfast: seed → 1-hop → passages → one draft. expert: seed → draft(graph) ∥ live → merge → skeptics (skeptics default on in config and Settings)
research_deskangles → parallel research → reduce → verify → report
discoveryloop-until-dry → verify → candidate write-back
promote_reviewsample candidates → skeptics → promote/reject
memory_groomdirty subjects → bounded grooming → refreshed projections

Ask UX: prose facts (not Cypher in the bubble), citations in a drawer, live progress in the workflow trace, refined answer collapsed. See Ask, chat, and live sources.

The implemented HTTP surface is summarized in the API reference.

5. Query API / agent tools#

Tools (MCP or local functions):

ToolBehavior
kg_search_entities(q, type?)Lexical / simple embed over names+descriptions
kg_neighbors(entity_id, hops=1..3)Bounded expansion
kg_ask(question)POST /v1/chat/completions effort=fast — one retrieve + one draft
kg_ingest(path_or_text)Enqueue extract workflow
kg_promote(candidate_ids)Human/verifier promotion

Answers must include citations: [{ src, predicate, dst, source_doc }].

End-to-end flows#

Ingest flow#

  1. Connector writes Document to staging.
  2. Workflow fans out extract per doc/chunk (cheap model).
  3. Barrier: gather candidates.
  4. resolve per type (strong model).
  5. Deterministic assemble into candidate edges/nodes.
  6. Optional verifier sample or human review → verified.

Ask flow#

Two HTTP efforts share the same retrieve path (see the API reference):

  • fast (POST /v1/chat/completions default, POST /v1/search) — seed → 1-hop → passages → one draft. No rewrite, live research, or skeptics.
  • evidence (POST /v1/ask/evidence) — the same retrieve pack, no draft.
  • expert (product UI via POST /v1/ui-chat, or effort=expert) —
  1. Entity search seeds from question.
  2. Expand ≤ N hops.
  3. Serialize triples (+ provenance).
  4. Strong model answers with mandatory citations.
  5. Skeptic panel filters citations vs graph; it defaults on and can be changed in Settings.
  6. Return answer; new live ingest lands as candidates (not auto-verified).

Discovery flow (optional)#

Unknown-size sweep over corpus or open web notes: loop-until-dry with seen set persisted in run ledger; verify before confirm; hard caps.

Minimal stack (implemented)#

LayerChoice
LanguageRust (doso-core) + Next.js UI
LLMProvider-agnostic OpenAI-compatible client (roles: extract/resolve/chat/skeptic)
GraphSQLite or Postgres adjacency tables
Workflowswgraph + named workflows registry
SecretsSettings DB (+ optional .env seed)

No LangGraph/Neo4j required; interfaces stay swappable.

Evaluation#

  • Gold entities/relations on a small personal fixture set → P/R/F1.
  • Grounded Q&A set: citation present + factually supported.
  • Cost ledger per workflow run.
  • Spot-check resolver merges weekly.

Open questions#

  • Entity type system for “personal” domains (health, finance) — privacy boundaries?
  • Hybrid vector index for seed retrieval — when does it pay off? Paid off as Ask’s document-chunk arm (not as memory). Graph edges stay extracted and typed.
  • Write-back from web research: default deny vs staging-only?
  • Multi-device sync / encryption at rest for personal corpora?

Security & privacy#

  • Treat the graph as sensitive personal data.
  • Never commit API keys, exports, or media/.
  • Rotate any key pasted into chat (xAI/OpenAI/Anthropic).
  • The graph lives with the instance. Cloud LLM calls send document text, and the product must make that tradeoff clear.

System guarantees#

  1. Ingest a folder of markdown → connected graph with provenance.
  2. Answer a multi-hop question with edge citations.
  3. Run a capped research diamond that fact-checks claims against the graph.
  4. Survive process restart without losing confirmed triples.