Skip to Content
How it works

How it works

A map of the machinery, for anyone who wants to know what the app is doing when they click.

One project, one folder

Every project is a folder on this Mac, inside the app’s sandbox container:

~/Library/Containers/app.syncretic.Syncretic/Data/Library/Application Support/Syncretic/ Models/ embeddinggemma-2-Q8_0.gguf the embedding model, shared by every project (310 MB, downloaded once) Projects/<uuid>/ project.json name, the shape as drawn, and the shape the database currently has graph.lbug the LadybugDB database: every node and relationship layout.json where nodes sit on the canvas, which are pinned, which are on the canvas history.json questions asked, the Cypher that answered them, routing decisions embeddings.json + embeddings.vec passages and their vectors, for vector search titles.* every node's title as a vector, always on, for duplicate spotting previews/ link thumbnails and metadata notes/, attachments the project's notes and their pictures, videos and drawings conversations Build with AI conversations, one file each publish.json where the project was shared, if it was

Settings › Storage › Show in Finder opens the Projects folder. Nothing in it is synced or uploaded.

The database

The graph is stored in LadybugDB, the maintained fork of the Kuzu embedded graph database: one in-process engine, one file, no server. It speaks the Kuzu dialect of Cypher, which is what every query in the app, written by you or by the AI, runs in.

The shape maps onto database tables directly:

In the shapeIn the database
A node type called Free energy principleCREATE NODE TABLE FreeEnergyPrinciple(id STRING PRIMARY KEY, …)
A relationship type called relates to between Concept and Concept, and Concept and SourceCREATE REL TABLE RELATES_TO(FROM Concept TO Concept, FROM Concept TO Source, id STRING, …)
A Text or Link or Enum property Year publisheda STRING column year_published
A Number propertya DOUBLE column
A Boolean propertya BOOLEAN column

Every node and relationship gets an id: a UUID the app generates. What makes a string column a Link or an Enum lives in project.json, not in the database.

There is no separate “finalize” step. The database exists from the moment a project is created, and every change to the shape goes straight into it with ALTER TABLE, CREATE and DROP statements the app writes. Changes that would delete data ask first. See Shape › How the database follows the shape.

How names become identifiers

Display names are free-form; database names are derived from them, and the app shows you the display name everywhere.

Display nameIdentifierRule
free energy principle, freeEnergy principleFreeEnergyPrinciplenode tables are PascalCase; camelCase is split into words
relates toRELATES_TOrelationship tables are UPPER_SNAKE
containsCONTAINS_ (shown as CONTAINS)a Cypher keyword gets a trailing underscore in the database only
Year publishedyear_publishedproperties are snake_case
2nd edition_2ndEditiona leading digit gets an underscore
ārjavamArjavamaccents are folded; only ASCII letters and digits survive

Two types whose names resolve to the same identifier are a validation issue (“resolve to the same name in the database”), and the shape waits until it’s fixed.

The AI route

Syncretic never holds an API key. It talks to the command-line tool of the subscription you already pay for:

  • Claude through Claude Code, signed in to a Claude Pro or Max plan.
  • ChatGPT through Codex, signed in to a ChatGPT Plus, Pro, Business or Enterprise plan.

Each call is one run of that tool with a structured-output request, so the answer comes back as JSON the app can check. The models offered are the ones the tool offers: Opus, Fable, Sonnet and Haiku for Claude (with efforts from Low to Max), and whatever Codex lists in its own model cache for ChatGPT. Two models are chosen in Settings: one that writes queries and builds, and one that routes questions in Auto search (a small call, so a quick model at low effort by default).

What goes out is deliberately small:

  • For a question: the question, plus the shape rendered as text (node tables and their columns, relationship tables and the pairs they connect, which column is each type’s title), plus the dialect notes and rules the app always sends. Not a single node.
  • For routing: the same, plus the list of embedded properties.
  • For building: your message, the text of any links and files you attached (read on this Mac first), your pictures, the shape, and, only if you turn Existing nodes on, the names and types of nodes so Claude can connect to them.

Answers are checked before they touch anything. A query runs through a keyword guard, then inside a read-only transaction. A draft from the builder is checked against the shape, matched against existing nodes, and shown to you; nothing is written until you add it.

Searching by meaning

Mark a text property as embedded and its values are split into passages (about a paragraph each, sentence-packed to roughly 1,000 characters, with a one-sentence overlap) and turned into vectors by EmbeddingGemma 2, Google’s 270M-parameter text embedding model, in 8-bit, running through llama.cpp with Metal. The node’s title is embedded alongside each passage, as the model was trained.

The vectors live in a sidecar beside the database, not in it, so queries the AI writes never see chunk rows. A reconciler keeps the index in step with the graph: on open, after every edit, when the shape changes and when the model finishes downloading, it works out what’s missing or stale and a background pass fills it in, saving as it goes. The top bar shows “Embedding 12 of 340” while it runs; whatever was left when the app quit is finished on the next open. Search is brute-force cosine similarity over the vectors with Accelerate, which takes under a millisecond for thousands of passages.

The canvas

The canvas shows only what you’ve explored, never the whole graph: a node is on the canvas when it has a position there. Graphs of up to 300 nodes open with everything placed; larger ones start empty and offer a place to begin. Nodes are laid out by a force simulation (many-body repulsion, link springs, collision) that runs off the main thread; nodes you drag are pinned where you put them. Positions, pins, and what’s on the canvas are saved in layout.json.

The search bar’s answer has a canvas of its own, separate from the one you’ve explored, so an answer never scatters what you laid out. Each canvas also keeps its own Arrange lens.

Other AI apps

A small helper, syncretic-mcp, ships inside the app bundle. An AI app (the Claude app, Claude Code, Codex, Cursor…) starts it as an MCP server; it opens Syncretic if needed and passes messages to the running app over the Mac’s loopback interface. The tools it offers read the open project, run read-only Cypher, and propose changes that arrive in the Build panel as a draft for you to review. See Other AI apps.

The code, in outline

For the curious: the app is SwiftUI only (App/, Shape/, Graph/, Ask/, Build/, Notes/, Embedding/, MCP/, Canvas/, Links/, Library/, DesignSystem/), and everything with no UI lives in a Swift package, SyncreticKit: the schema model, the store and DDL builder, the Cypher guard, the AI client with its Claude Code and Codex transports, the query router, the graph builder and shape designer, the MCP protocol and tools, text chunking, the llama.cpp embedder, the vector index and reconciler, link metadata, project files, force layout and the arrangement geometry. The theme is called Lumen: ideas as points of light, on warm ink (dark) or parchment (light).