Skip to main content

Database Sync

The indexer syncs account data into PostgreSQL so the relayer can resolve ownership quickly without hitting the blockchain on every request.

What database tables does the indexer use?​

Both the relayer and the indexer connect to the same PostgreSQL instance (with the pgvector extension enabled). Migrations run automatically on boot.

vector_entries​

The primary search table, stores vector embeddings linked to encrypted Walrus blobs.

ColumnTypeDescription
idTEXT (PK)UUID for this entry
ownerTEXTOwner's Sui address
namespaceTEXTNamespace label (default: "default")
blob_idTEXTWalrus blob ID pointing to the encrypted payload
embeddingvector(1536)1536-dimensional vector embedding (pgvector)
created_atTIMESTAMPTZInsertion timestamp

Indexes:

  • idx_vector_entries_owner, B-tree on owner
  • idx_vector_entries_blob_id, B-tree on blob_id
  • idx_vector_entries_owner_ns, composite B-tree on (owner, namespace) for scoped queries
  • idx_vector_entries_embedding, HNSW on embedding using vector_cosine_ops for fast similarity search

delegate_key_cache​

Auth optimization, caches the mapping from delegate public key to account, so the relayer doesn't need to scan the onchain registry on every request.

ColumnTypeDescription
public_keyTEXT (PK)Hex-encoded Ed25519 public key
account_idTEXTMemWalAccount object ID
ownerTEXTOwner's Sui address
cached_atTIMESTAMPTZWhen this mapping was cached

The cache is populated lazily during auth. If a cached entry becomes stale (key was removed onchain), the relayer re-resolves from the chain and updates the cache.

accounts​

Populated by the indexer, maps owner addresses to their MemWalAccount object IDs.

ColumnTypeDescription
account_idTEXT (PK)MemWalAccount object ID
ownerTEXT (unique)Owner's Sui address
created_atTIMESTAMPTZWhen this row was indexed

indexer_state​

A legacy table from the event-polling indexer. The relayer's initial migration still creates it, but the current indexer does not write to it: it records its progress as a checkpoint watermark through the Sui checkpoint-ingestion framework.

ColumnTypeDescription
keyTEXT (PK)State key (for example, "event_cursor")
valueTEXTJSON-serialized cursor (txDigest + eventSeq)

Why does the indexer sync data to PostgreSQL?​

  • Constant-time account lookup, the relayer checks delegate_key_cache and accounts instead of scanning the onchain registry
  • Resumable ingestion, the indexer records its checkpoint watermark, so it picks up where it left off after restarts without re-processing old checkpoints
  • Reactive cleanup, when Walrus returns 404 for an expired blob during recall, the relayer deletes the corresponding vector_entries rows automatically

How does similarity search work?​

Recall queries use pgvector's cosine distance operator (<=>) against the HNSW index:

Source: indexer/database-sync.md
SELECT blob_id, (embedding <=> $1)::float8 AS distance
FROM vector_entries
WHERE owner = $2 AND namespace = $3
ORDER BY embedding <=> $1
LIMIT $4

The HNSW index provides approximate nearest neighbor search, which is fast enough for interactive recall even with large numbers of stored memories.

Recall limits and distance cutoffs​

The relayer fills $4 from the request limit. The limit defaults to 10, and the relayer caps it with body.limit.min(100) so a request cannot scan the whole table. The relayer applies no relevance threshold, so the query returns the closest matches even when they are weak. To drop weak matches, pass maxDistance to the SDK recall call. The SDK then removes results where distance >= maxDistance on the client side.

Where migrations live​

No migrations table and no manual sqlx migrate step exist. The SQL files live in services/server/migrations. VectorDb::new() in services/server/src/storage/db.rs lists each file explicitly and embeds it through include_str!. Every listed file re-runs in order on every server boot against DATABASE_URL, so restarting the server reapplies them. For this reason, each statement is idempotent and uses IF NOT EXISTS, IF EXISTS, or a guard query. When a file fails, the server reports Failed to run migration with the file name.