Behaviour for unified graph persistence and querying backends.
Implementations handle storing, retrieving, and querying knowledge graph nodes through a single interface, replacing the separate Storage and in-memory Graph modules with a database-agnostic contract.
Callbacks
init/1- Initialize the backend with configuration options.apply_changeset/2- Persist a batch of node additions and links.get_ingestion/2- Fetch a durable ingestion record by source ID.commit_ingestion/3- Compare-and-set graph changes and an ingestion record, reporting whether it inserted or found an equal record.delete_nodes/2- Remove nodes by their IDs.find_candidates/6- Query for nodes matching type/embedding/tag criteria.get_node/2- Fetch a single node by ID.get_linked_nodes/3- Fetch linked nodes, optionally filtered by edge type.
Read callbacks (find_candidates, get_node, get_linked_nodes, get_ingestion)
return state
for interface uniformity but must not rely on state mutation — callers may
discard the returned state in read-only contexts.
Backends used with access control must persist NodeMetadata.audience, preserve
it during usage updates and consolidation, and reject changes to an assigned
audience. An absent audience is legacy, unclassified data. Protected reads
enumerate built-in node types to create an authorized snapshot before scoring.
Summary
Types
@type ingestion_record() :: %{ optional(:audience) => Mnemosyne.AccessControl.audience() | nil, source_id: String.t(), payload_digest: binary(), fingerprint_version: pos_integer(), receipt: Mnemosyne.IngestionReceipt.t() }
@type state() :: term()
Callbacks
@callback apply_changeset(Mnemosyne.Graph.Changeset.t(), state()) :: {:ok, state()} | {:error, Mnemosyne.Errors.error()}
@callback commit_ingestion(ingestion_record(), Mnemosyne.Graph.Changeset.t(), state()) :: {:ok, :inserted | :existing, Mnemosyne.IngestionReceipt.t(), state()} | {:error, Mnemosyne.Errors.error()}
@callback delete_nodes([String.t()], state()) :: {:ok, state()} | {:error, Mnemosyne.Errors.error()}
@callback get_ingestion(String.t(), state()) :: {:ok, ingestion_record() | nil, state()} | {:error, Mnemosyne.Errors.error()}
@callback get_linked_nodes([String.t()], Mnemosyne.Graph.Edge.edge_type() | nil, state()) :: {:ok, [struct()], state()}
@callback get_nodes_by_type(node_types :: [atom()], state()) :: {:ok, [struct()], state()} | {:error, Mnemosyne.Errors.error()}
@callback init(opts :: keyword()) :: {:ok, state()} | {:error, Mnemosyne.Errors.error()}
@callback update_metadata(%{required(String.t()) => struct()}, state()) :: {:ok, state()} | {:error, Mnemosyne.Errors.error()}