The database where storage, context, and memory are one transaction.
MULTI-MODEL
Five databases become one
Stop stitching databases together. SurrealDB unifies your entire context layer in a single multi-model engine - one query language, one transaction, one deployment.
Before - Multiple databases
5 SDKs · 5 drivers · 5 connection pools
Vector
database
Proprietary API
Full-text search
database
Search DSL
Time-series
database
InfluxQL / Flux
Relational / document
database
SQL / MQL
Graph
database
Cypher / Gremlin
Eventual consistency only
After - SurrealDB
1 SDK · 1 driver · 1 connection pool
Single ACID transactionStrong consistency
THE PLATFORM
The only vertical stack
from storage to memory
No other product offers this. Object storage to agent memory. One transaction boundary. One permission model. One deployment.

THE ARCHITECTURE
One stack. Four layers.
Every layer under one roof.
Spectron gives agents persistent memory. SurrealDB unifies every data model in one ACID transaction. The storage engine separates compute from storage on commodity object storage. No glue code. No middleware.
Entity extraction
Knowledge graph
Temporal facts
Hybrid retrieval
Documents
Graphs
Vectors
Time-series
Auth
APIs
Quorum consensus
Compute-storage sep.
Scale to zero
Object storage
(S3 / GCS / Azure Blob)
WHY IT BREAKS
Agents fail due to context.
Not models.
Your model can reason. It just has nothing reliable to reason over. Five systems. Five consistency models. Context that fragments at every seam.
Context that leaks at every seam
Data flows between five systems. Relationships, history, and metadata fragment at every boundary.
Data fragments at every system boundary
Writes that half-succeed
Memory updates in one system, state fails in another. Without unified transactions, partial writes corrupt context.
Single transaction
committed
inconsistent state
rolled back
Latency that compounds
Every system adds a network hop. Round trips stack under load until agents miss their response window.
Five systems to keep alive
Five failure modes, five monitoring setups, five sets of credentials. The glue code becomes the product.
Vector DB
Similarity search
Graph DB
Relationship store
Doc store
Raw document index
Auth service
Identity & tokens
Five systems. Five auth configs. Five things to scale, secure, and keep alive.
HOW IT WORKS
The read-think-write loop
Read, think, write - in a single transaction.
WHY SURREALDB
Every failure has a structural fix.
Each failure mode has a structural fix - built into the engine, not bolted on top.
FIXES: CONTEXT LEAKS
Multi-model in one engine
Documents, graphs, vectors, and time-series are native primitives in one query. No stitching across systems.

FIXES: PARTIAL WRITES
ACID across all data models
Graph updates, document writes, and vector index changes happen in a single transaction. All succeed or none do.

FIXES: COMPOUNDING LATENCY
One query, one round trip
SurrealQL composes graph traversal, vector search, and temporal filtering in a single statement. No multi-system orchestration.

FIXES: OPERATIONAL OVERHEAD
Built-in backend
Auth, permissions, API endpoints, live queries, and event triggers. The database is the backend.

SURREALQL
One query. Every model.
Graph traversals, vector search, transactions, and access control in one expressive language.
MULTI-MODEL QUERY
Graph, vector, and temporal data in one round trip.
LET $vec = fn::embed("running shoes");SELECT
->purchased->product AS history,
->reviewed->product[WHERE
vector::similarity::cosine(
embedding, $vec
) > 0.8
] AS relevant,
->prefs[WHERE valid_at <= time::now()] AS prefs
FROM ONLY $user;
ACID TRANSACTIONS
Multi-model writes succeed together or not at all.
BEGIN TRANSACTION;LET $order = CREATE order SET
user = $auth.id,
product = product:headphones,
total = product:headphones.price;RELATE $auth.id->purchased->product:headphones
SET at = time::now();UPDATE product:headphones SET stock -= 1;
COMMIT TRANSACTION;
BUILT-IN AUTH
Authentication and row-level security in the database.
DEFINE ACCESS account ON DATABASE
TYPE RECORD
SIGNUP (
CREATE user SET
email = $email,
pass = crypto::argon2::generate($pass)
)
SIGNIN (
SELECT * FROM user WHERE
email = $email AND
crypto::argon2::compare(pass, $pass)
);DEFINE TABLE order PERMISSIONS
FOR select WHERE user = $auth.id
FOR create WHERE $auth.id != NONE;
Explore real-world query patterns across search, graphs, agents, and more.
-- Hybrid RAG retrieval. Blends vector cosine similarity with BM25
-- full-text search into a single weighted score, so results are
-- both semantically relevant and keyword-accurate.-- Step 1: Embed the user's natural language query
LET $query = "How do I configure access control?";
LET $embedding = fn::embed($query);-- Step 2: Run a hybrid search combining vector and full-text scores
SELECT
id, title, content,
-- Compute semantic similarity via cosine distance
vector::similarity::cosine(embedding, $embedding) AS vs,
-- Compute keyword relevance via full-text scoring
search::score(1) AS ts,
-- Blend both signals into a single weighted score
(vs * 0.6) + (ts * 0.4) AS score
FROM document
-- Union of both retrieval methods — score blending handles ranking
WHERE embedding <|4,COSINE|> $embedding
OR content @1@ $query
ORDER BY score DESC
LIMIT 5;
-- Graph-enhanced RAG with re-ranking. Over-fetches candidates via
-- hybrid search, then uses graph connectivity to boost documents
-- that are well-cited within the candidate set — surfacing
-- authoritative context that pure similarity would underrank.-- Step 1: Embed the query
LET $query = "How do I configure access control?";
LET $embedding = fn::embed($query);-- Step 2: Over-fetch candidates via hybrid vector + full-text search
LET $candidates = SELECT
id, title, content,
vector::similarity::cosine(embedding, $embedding) AS vs,
search::score(1) AS ts,
(vs * 0.6) + (ts * 0.4) AS score
FROM document
WHERE embedding <|10,COSINE|> $embedding
OR content @1@ $query
ORDER BY score DESC
LIMIT 10;-- Step 3: Re-rank using graph authority within the candidate set
LET $ids = $candidates.id;
SELECT
id, title, content, score,
-- Count inbound references from other candidates (graph authority)
count(<-references<-document[WHERE id IN $ids]) AS authority,
-- Blend retrieval relevance with graph connectivity
(score * 0.7) + (authority * 0.15) AS final_score
FROM $candidates
ORDER BY final_score DESC
LIMIT 5;
-- Graph context expansion. Finds seed documents via hybrid search,
-- then walks the document reference graph to surface related context
-- that pure vector or keyword search would miss.-- Step 1: Embed the query
LET $query = "How do I configure access control?";
LET $embedding = fn::embed($query);-- Step 2: Find seed documents via hybrid vector + full-text search
LET $seeds = SELECT
id, title, content,
vector::similarity::cosine(embedding, $embedding) AS vs,
search::score(1) AS ts,
(vs * 0.6) + (ts * 0.4) AS score
FROM document
WHERE embedding <|4,COSINE|> $embedding
OR content @1@ $query
ORDER BY score DESC
LIMIT 3;-- Step 3: Traverse the document graph to pull in related context
RETURN {
-- The seed documents found by hybrid search
matches: $seeds,
-- Walk outbound edges to find documents these seeds cite
referenced: $seeds->references->document,
-- Walk inbound edges to find documents that cite these seeds
referenced_by: $seeds<-references<-document
};
-- Knowledge graph modelling. Entities are records with vector
-- embeddings, connected by typed graph edges that carry their own
-- properties. Multi-hop traversals query the graph in one statement.-- Step 1: Create entity records with embeddings for semantic search
CREATE entity:openai SET
name = "OpenAI", type = "organisation",
embedding = fn::embed("OpenAI AI research company");CREATE entity:gpt4 SET
name = "GPT-4", type = "model",
embedding = fn::embed("GPT-4 large language model");-- Step 2: Connect entities with typed graph edges
RELATE entity:openai->developed->entity:gpt4 SET
at = d"2023-03-14", context = "Released as a multimodal LLM";-- Step 3: Query with multi-hop traversals across the graph
SELECT
name,
-- Follow outbound "developed" edges to find what OpenAI built
->developed->entity.name AS built,
-- Continue traversal: who uses what OpenAI built?
->developed->entity<-used_by<-entity.name AS users,
-- Follow a different edge type for partnerships
->partnered_with->entity.name AS partners
FROM entity:openai;
-- Agent memory with state invalidation. Memories carry TTLs and
-- vector embeddings. An event automatically detects when a new fact
-- supersedes an older one via semantic similarity, and links them
-- with a graph edge. Recall uses recency-weighted scoring so newer
-- versions of a fact naturally rank higher.-- Step 1: Store a memory with a TTL and link the agent to it
CREATE memory SET
agent = agent:main, content = "User prefers dark mode UI",
embedding = fn::embed("User prefers dark mode UI"),
created_at = time::now(), valid_until = time::now() + 7d;RELATE agent:main->recalls->memory:last SET strength = 1.0, at = time::now();
-- Step 2: Auto-detect superseded facts on every new memory
DEFINE EVENT detect_supersedes ON memory WHEN $event = "CREATE" THEN {
-- Find existing memories that are semantically near-identical
LET $similar = SELECT id FROM memory
WHERE id != $after.id
AND vector::similarity::cosine(embedding, $after.embedding) > 0.92;
-- Link the new memory to each older version it supersedes
FOR $old IN $similar {
RELATE $after.id->supersedes->$old.id SET at = time::now();
};
};-- Step 3: Recall memories with recency-weighted relevance
LET $context = fn::embed("What are the user preferences?");
SELECT
content, created_at,
vector::similarity::cosine(embedding, $context) AS semantic,
-- Walk the supersedes graph to see what this fact replaced
->supersedes->memory.content AS supersedes,
-- Walk back to find which agent stored this memory
<-recalls<-agent.id AS source,
-- Recency decay so newer facts naturally outrank older versions
semantic * math::pow(math::E, -0.1 * duration::days(time::now() - created_at)) AS score
FROM memory
WHERE embedding <|4,COSINE|> $context
AND valid_until > time::now()
ORDER BY score DESC
LIMIT 10;
-- Conversational memory. Sessions and messages are linked by graph
-- edges, making it easy to retrieve full threads in order. Each
-- message carries a vector embedding so past conversations can be
-- searched semantically across all sessions.-- Step 1: Create a conversation session for the current user
LET $session = CREATE session SET
user = $auth.id, started_at = time::now(), topic = "Project planning";-- Step 2: Store a message and link it to the session via graph edge
LET $msg = CREATE message SET
role = "user", content = "What tasks are left for launch?",
embedding = fn::embed("What tasks are left for launch?"), at = time::now();RELATE $session->contains->$msg SET position = 1;
-- Step 3: Retrieve the full conversation thread via graph traversal
SELECT role, content, at FROM $session->contains->message
ORDER BY at ASC;-- Step 4: Semantic search across all past conversations
LET $q = fn::embed("launch checklist");
SELECT
content,
-- Walk the graph back to find which session this message belongs to
<-contains<-session.topic AS session,
vector::similarity::cosine(embedding, $q) AS relevance
FROM message
WHERE embedding <|4,COSINE|> $q
ORDER BY relevance DESC
LIMIT 5;
-- Live queries. Subscribe to real-time changes on any table with
-- optional filters. The database pushes notifications on every
-- CREATE, UPDATE, and DELETE that matches the query. Diff mode
-- sends only the changed fields instead of the full record.-- Step 1: Subscribe to new tasks assigned to the current user
LIVE SELECT * FROM task
WHERE assignee = $auth.id AND status = "open";-- Step 2: Monitor memory changes across all agents
LIVE SELECT content, valid_until,
-- Walk the graph to include which agent owns each memory
<-recalls<-agent.id AS agent
FROM memory;-- Step 3: Watch for new messages arriving in a specific session
LIVE SELECT role, content, at
FROM message
WHERE <-contains<-session = session:active;-- Step 4: Diff mode — receive only changed fields instead of the full record
LIVE SELECT DIFF FROM task WHERE status != "closed";
-- Collaborative filtering. Uses multi-hop graph traversal to find
-- items liked by users with similar taste, then re-ranks those
-- candidates by vector similarity to the user's stated preference.-- Step 1: Traverse the graph to find candidate items
LET $candidates = SELECT
->liked->item<-liked<-user->liked->item AS recommended
FROM ONLY $auth.id;-- Step 2: Embed the user's preference for re-ranking
LET $pref = fn::embed("lightweight running shoes");
-- Step 3: Re-rank candidates by blending relevance and popularity
SELECT
id, name, description,
vector::similarity::cosine(embedding, $pref) AS relevance,
-- Count how many users liked each item
count(<-liked<-user) AS popularity
FROM $candidates.recommended
-- Exclude items the user has already liked
WHERE id NOT IN $auth.id->liked->item.id
ORDER BY relevance * 0.7 + popularity * 0.3 DESC
LIMIT 10;
-- Geospatial queries. Proximity lookups powered by geographic
-- distance functions, combined with graph traversals to pull in
-- related data like maintenance history or recent sensor readings.-- Step 1: Create devices with geographic coordinates
CREATE device:sensor_01 SET
name = "Temperature sensor", location = (51.5074, -0.1278),
status = "active", last_reading = 22.5;CREATE device:sensor_02 SET
name = "Humidity sensor", location = (51.5080, -0.1260),
status = "active", last_reading = 65.2;-- Step 2: Find all devices within 5km of a point
LET $point = (51.5074, -0.1278);
SELECT name, status, last_reading,
geo::distance(location, $point) AS distance_m
FROM device
WHERE geo::distance(location, $point) < 5000
ORDER BY distance_m ASC;-- Step 3: Combine spatial proximity with graph traversal
SELECT name,
geo::distance(location, $point) AS distance_m,
-- Walk inbound edges to find who maintains this device
<-maintains<-technician.name AS maintained_by,
-- Walk outbound edges to get recent sensor readings
->reports->reading[WHERE at > time::now() - 1d] AS recent
FROM device
WHERE geo::distance(location, $point) < 2000;
-- Event-driven automation. Define triggers that fire side effects
-- when data changes. Events can generate embeddings, cascade
-- status updates through the graph, or log changes to an audit
-- trail, all without application-level code.-- Step 1: Auto-generate embeddings whenever a document is created
DEFINE EVENT embed_on_create ON document WHEN $event = "CREATE" THEN {
UPDATE $after.id SET embedding = fn::embed($after.content);
};-- Step 2: Cascade status — when a task completes, check if the parent project is done
DEFINE EVENT task_completed ON task
WHEN $event = "UPDATE" AND $before.status != "done" AND $after.status = "done"
THEN {
-- Walk the graph to find the parent project
LET $project = $after.id<-contains<-project[0];
-- Count remaining open tasks under that project
LET $open = count($project->contains->task[WHERE status != "done"]);
-- If none remain, mark the project as complete
IF $open = 0 { UPDATE $project SET status = "complete"; };
};-- Step 3: Log every change to an immutable audit trail
DEFINE EVENT audit_trail ON document
WHEN $event IN ["CREATE", "UPDATE", "DELETE"]
THEN {
CREATE audit SET table = "document", action = $event,
record = $after.id ?? $before.id, at = time::now(), by = $auth.id;
};
-- Multi-tenant access control. Record-based authentication with
-- row-level permissions that scope data to organisations and
-- agents. Users only see their own tenant's data, and agents
-- can only access their own memories.-- Step 1: Define record-based signup and signin
DEFINE ACCESS account ON DATABASE TYPE RECORD
SIGNUP (
CREATE user SET email = $email, org = $org,
pass = crypto::argon2::generate($pass)
)
SIGNIN (
SELECT * FROM user
WHERE email = $email AND crypto::argon2::compare(pass, $pass)
);-- Step 2: Tenant-scoped permissions on the project table
DEFINE TABLE project PERMISSIONS
FOR select, update WHERE org = $auth.org
FOR create WHERE $auth.id != NONE
-- Only admins can delete projects
FOR delete WHERE org = $auth.org AND $auth.role = "admin";-- Step 3: Agent-scoped permissions on the memory table
DEFINE TABLE memory PERMISSIONS
FOR select, create, update WHERE agent = $auth.id
-- Agents can only delete their own expired memories
FOR delete WHERE agent = $auth.id AND valid_until < time::now();
USE CASES
Built for agents
Context-aware agents, persistent memory, knowledge graphs, real-time collaboration - see what teams are building on the context layer.
AI agents
Build context-aware agents with unified memory, knowledge graphs, and structured context in one system.
Agent memory
Persistent, structured memory - working, semantic, episodic, and procedural - for agents that remember.
Knowledge graphs
Model rich relationships and dependencies. Traverse connections, rank entities, and retrieve structured context in a single query.
Real-time applications
Live queries push changes to subscribers as they commit. No polling, no message broker, no stale reads.
GET STARTED
Start building with the context layer
Object storage to agent memory. A single stack, a single transaction, zero glue code.