graph_nodes and graph_edges. You can SELECT against it, join it to your operational data, and inspect it in any Postgres client.
No Neo4j. No graph database to sync. One database for everything.
Node structure
Each node is one row ingraph_nodes:
Stable per-node identifier.
Tenant isolation — every query filters on this.
Project-level scoping within a tenant.
Typed category:
person, email_thread, slack_message, meeting, file, folder, domain.Origin connector:
gmail, slack, fireflies, google_drive, imap, postgres, mysql.External ID at the source (e.g. Gmail thread ID, Slack message timestamp).
Display title. Weighted
A in the BM25 index.Full text. Weighted
B in the BM25 index.When the underlying event happened.
pgvector column for semantic search.
Full-text search column maintained by trigger.
Source-specific fields.
Typed edges
Each edge is one row ingraph_edges with source_id, target_id, relation, weight, source_date, and metadata.
sent_by — Message authored by a person
sent_by — Message authored by a person
replied_to — Response relationship
replied_to — Response relationship
Message B is a reply to message A. Enables thread reconstruction without relying on provider threading.
in_thread — Message belongs to a thread
in_thread — Message belongs to a thread
Individual messages link to their parent thread node. Lets “summarize this thread” work across sources.
posted_in — Slack message posted in a channel
posted_in — Slack message posted in a channel
Slack message → channel node. Enables
#channel-scoped search.attended — Person attended a meeting
attended — Person attended a meeting
Fireflies meeting → person. Foundation for “who was in the Phoenix kickoff?”
organized_by — Meeting organizer
organized_by — Meeting organizer
Meeting node → organizer person. Different from
attended — tracks initiative.participant — Person involved in a conversation
participant — Person involved in a conversation
Generic participation edge, used for email threads with recipients who aren’t sender.
in_folder — File lives in a folder
in_folder — File lives in a folder
Drive file → parent folder. Enables folder-scoped queries.
from_domain — Person's email domain
from_domain — Person's email domain
Person node → domain node. Surfaces cross-domain relationships (internal vs external).
has_email — Person linked to email address
has_email — Person linked to email address
Person entity → the
person:email@company.com alias. Enables entity resolution.follow_edges(node_id) returns all edges where the node is either source_id or target_id.
Why typed edges matter
Glean’s Enterprise Graph is a closed system — you can’t query it, inspect it, or join it with other data. Fabric’s graph is two Postgres tables. The structure is the point.
attended the Phoenix kickoff on April 7, sent_by three emails about the launch, and replied_to the legal review thread.
Typed edges turn “find similar text” into “reason about who, when, and why.”
Querying the graph
- From chat
- From the REST API
- From Postgres directly
Natural-language questions implicitly traverse the graph.
Q: Who has context on the billing migration?Fabric finds the X topic node, follows
attended, participant, and sent_by edges outward, and ranks the returned people by edge weight and recency.Multi-hop traversal
The reasoning loop walks 2–3 hops out from a seed node via Postgres recursive CTE:Who has worked with Cole on billing-related topics?
Cole → threads where Cole is
participant → other people participant in those same threads.What decisions were made about Phoenix?
Phoenix topic → meetings
attended for Phoenix → observations linked to those meetings.Graph explorer
The explorer in the web UI is an interactive visualization — search for any entity, expand its neighborhood, filter by edge type, navigate by clicking.Verify extraction
Confirm new connectors are producing the edges you expect.
Audit answers
Understand “why did the answer include this person?”
Explore unknowns
Find connections you didn’t know existed.
Graph maintenance
Entities and edges update on each sync. Deleted source content triggers node and edge removal on the next run. Edge weights decay slightly per week unseen, so old relationships fade unless reinforced by new activity.Why Postgres and not Neo4j
Fabric uses Postgres with pgvector for everything — graph, memory, vector search, full-text search, operational data.One database
No sync overhead between a graph DB and a relational DB.
Recursive CTEs
Fast enough for 2–3 hop traversal at current scales.
pgvector mature
HNSW indexes, cosine distance, production-grade.
Inspectable
Any Postgres client, any BI tool, any ORM you already run.