Skip to main content
Membrane can be used directly as a Go package, embedding the full memory substrate—storage, ingestion, retrieval, decay, revision, and metrics—into your own process.

Installation

go get github.com/GustyCube/membrane

Creating a Membrane instance

Start by loading or constructing a Config, then pass it to membrane.New.
cfg := membrane.DefaultConfig()
cfg.DBPath = "my-agent.db"

m, err := membrane.New(cfg)
if err != nil {
    log.Fatal(err)
}

Starting and stopping

Start launches the background decay and consolidation schedulers. Stop gracefully shuts them down and closes the store.
ctx := context.Background()
if err := m.Start(ctx); err != nil {
    log.Fatal(err)
}
defer m.Stop()
Always call m.Stop() (or defer m.Stop()) to flush the store and release database locks before your process exits.

Ingestion methods

All ingestion calls return a *schema.MemoryRecord containing the assigned UUID, type, sensitivity, salience, and confidence.

IngestEvent

Captures a raw event (tool calls, errors, observations) as an episodic record.
rec, err := m.IngestEvent(ctx, ingestion.IngestEventRequest{
    Source:    "build-agent",
    EventKind: "tool_call",
    Ref:       "build#42",
    Summary:   "Executed go build, failed with linker error",
    Tags:      []string{"build", "error"},
})
fmt.Printf("Ingested: %s\n", rec.ID)
FieldTypeRequiredDescription
SourcestringYesActor or system that produced the event
EventKindstringYesEvent type, e.g. "tool_call", "error", "user_input"
RefstringYesSource event reference ID
SummarystringNoHuman-readable summary
Timestamptime.TimeNoDefaults to time.Now()
Tags[]stringNoLabels for categorization
ScopestringNoVisibility scope
Sensitivityschema.SensitivityNoOverrides default sensitivity

IngestToolOutput

Captures a tool invocation with its arguments and result as an episodic record. The payload includes a ToolGraph node for dependency tracking.
rec, err := m.IngestToolOutput(ctx, ingestion.IngestToolOutputRequest{
    Source:   "build-agent",
    ToolName: "go_build",
    Args:     map[string]any{"target": "./cmd/..."},
    Result:   map[string]any{"exit_code": 1, "stderr": "linker error"},
    Tags:     []string{"build"},
})
FieldTypeDescription
SourcestringActor that invoked the tool
ToolNamestringName of the tool invoked
Argsmap[string]anyArguments passed to the tool
ResultanyOutput produced by the tool
DependsOn[]stringIDs of tool nodes this output depends on
Tags[]stringLabels for categorization
ScopestringVisibility scope

IngestObservation

Captures a subject-predicate-object observation as a semantic record.
rec, err := m.IngestObservation(ctx, ingestion.IngestObservationRequest{
    Source:    "build-agent",
    Subject:   "user",
    Predicate: "prefers_language",
    Object:    "go",
    Tags:      []string{"preferences"},
})

IngestOutcome

Updates an existing episodic record with an outcome status. The target must be an episodic record.
rec, err := m.IngestOutcome(ctx, ingestion.IngestOutcomeRequest{
    Source:         "build-agent",
    TargetRecordID: episodicRecordID,
    OutcomeStatus:  schema.OutcomeStatusSuccess,
})

IngestWorkingState

Captures the current in-flight task state as a working memory record.
rec, err := m.IngestWorkingState(ctx, ingestion.IngestWorkingStateRequest{
    Source:      "build-agent",
    ThreadID:    "session-001",
    State:       schema.TaskStateExecuting,
    NextActions: []string{"run tests", "deploy"},
    OpenQuestions: []string{"which registry to push to?"},
})
FieldTypeDescription
SourcestringActor that produced the working state
ThreadIDstringThread or session identifier
Stateschema.TaskStateCurrent task state
NextActions[]stringPlanned next actions
OpenQuestions[]stringUnresolved questions
ContextSummarystringSummary of the current context
ActiveConstraints[]schema.ConstraintCurrently active constraints

Retrieve with trust context

Retrieve performs layered retrieval in canonical order: working → semantic → competence → plan_graph → episodic. The TrustContext gates which records are returned.
resp, err := m.Retrieve(ctx, &retrieval.RetrieveRequest{
    TaskDescriptor: "fix build error",
    Trust: &retrieval.TrustContext{
        MaxSensitivity: schema.SensitivityMedium,
        Authenticated:  true,
        ActorID:        "build-agent",
        Scopes:         []string{"project-acme"},
    },
    MemoryTypes: []schema.MemoryType{
        schema.MemoryTypeCompetence,
        schema.MemoryTypeSemantic,
    },
    MinSalience: 0.2,
    Limit:       10,
})
if err != nil {
    log.Fatal(err)
}

for _, r := range resp.Records {
    fmt.Printf("Found: %s (type=%s, confidence=%.2f)\n", r.ID, r.Type, r.Confidence)
}
To fetch a single record by ID:
record, err := m.RetrieveByID(ctx, recordID, &retrieval.TrustContext{
    MaxSensitivity: schema.SensitivityHigh,
    Authenticated:  true,
})

Revision operations

Revision operations update the record’s status and append an audit entry. All operations accept an actor and rationale for full provenance.

Supersede

Atomically replaces an old record with a new version.
newRec := &schema.MemoryRecord{ /* ... */ }
superseded, err := m.Supersede(ctx, oldRecordID, newRec, "agent", "Go version updated")

Fork

Creates a conditional variant derived from a source record.
forkedRec := &schema.MemoryRecord{ /* ... */ }
forked, err := m.Fork(ctx, sourceID, forkedRec, "agent", "different for dev environment")

Retract

Marks a record as retracted without deleting it. The record remains in the audit trail.
err := m.Retract(ctx, recordID, "agent", "no longer accurate")

Merge

Combines multiple records into one consolidated record.
mergedRec := &schema.MemoryRecord{ /* ... */ }
merged, err := m.Merge(ctx, []string{id1, id2, id3}, mergedRec, "agent", "consolidating duplicates")

Contest

Marks a record as contested when conflicting evidence exists.
err := m.Contest(ctx, recordID, conflictingRecordID, "agent", "new evidence contradicts this")

Reinforce and Penalize

Reinforce boosts a record’s salience; Penalize reduces it by a specified amount.
// Boost salience after a successful retrieval
err := m.Reinforce(ctx, recordID, "agent", "plan used successfully")

// Reduce salience when a procedure fails
err := m.Penalize(ctx, recordID, 0.2, "agent", "procedure led to build failure")

GetMetrics

Returns a point-in-time *metrics.Snapshot of the substrate.
snap, err := m.GetMetrics(ctx)
if err != nil {
    log.Fatal(err)
}
fmt.Printf("Total records: %d\n", snap.TotalRecords)
fmt.Printf("Retrieval usefulness: %.2f\n", snap.RetrievalUsefulness)
fmt.Printf("Competence success rate: %.2f\n", snap.CompetenceSuccessRate)

Config struct reference

Backend
string
Storage backend: "sqlite" (default) or "postgres".
DBPath
string
SQLite database file path. Default: "membrane.db".
PostgresDSN
string
PostgreSQL connection string. Required when Backend is "postgres". Falls back to MEMBRANE_POSTGRES_DSN.
ListenAddr
string
gRPC listen address. Default: ":9090".
DecayInterval
time.Duration
How often the decay scheduler runs. Default: 1h.
ConsolidationInterval
time.Duration
How often the consolidation scheduler runs. Default: 6h.
DefaultSensitivity
string
Default sensitivity assigned at ingestion. Default: "low". Valid values: public, low, medium, high, hyper.
SelectionConfidenceThreshold
float64
Minimum confidence for competence and plan_graph candidates in the selector. Default: 0.7.
EncryptionKey
string
SQLCipher key for the SQLite database. Falls back to MEMBRANE_ENCRYPTION_KEY.
EmbeddingEndpoint
string
HTTP endpoint for generating embeddings (Postgres only).
EmbeddingModel
string
Embedding model name sent to the embedding endpoint.
EmbeddingDimensions
int
Output dimension of the embedding model. Default: 1536.
EmbeddingAPIKey
string
API key for the embedding endpoint. Falls back to MEMBRANE_EMBEDDING_API_KEY.
LLMEndpoint
string
HTTP endpoint for semantic fact extraction during consolidation (Postgres only).
LLMModel
string
Chat model name for the LLM extraction endpoint.
LLMAPIKey
string
API key for the LLM endpoint. Falls back to MEMBRANE_LLM_API_KEY.
TLSCertFile
string
Path to TLS certificate PEM file. TLS is disabled when empty.
TLSKeyFile
string
Path to TLS private key PEM file.
APIKey
string
Shared secret for authenticating gRPC clients. Falls back to MEMBRANE_API_KEY. Authentication disabled when empty.
RateLimitPerSecond
int
Maximum requests per second per client. 0 disables rate limiting. Default: 100.

Complete example

package main

import (
    "context"
    "fmt"
    "log"

    "github.com/GustyCube/membrane/pkg/ingestion"
    "github.com/GustyCube/membrane/pkg/membrane"
    "github.com/GustyCube/membrane/pkg/retrieval"
    "github.com/GustyCube/membrane/pkg/schema"
)

func main() {
    cfg := membrane.DefaultConfig()
    cfg.DBPath = "my-agent.db"

    m, err := membrane.New(cfg)
    if err != nil {
        log.Fatal(err)
    }
    defer m.Stop()

    ctx := context.Background()
    m.Start(ctx)

    // Ingest an episodic event (tool call observation)
    rec, _ := m.IngestEvent(ctx, ingestion.IngestEventRequest{
        Source:    "build-agent",
        EventKind: "tool_call",
        Ref:       "build#42",
        Summary:   "Executed go build, failed with linker error",
        Tags:      []string{"build", "error"},
    })
    fmt.Printf("Ingested episodic record: %s\n", rec.ID)

    // Ingest a semantic observation
    m.IngestObservation(ctx, ingestion.IngestObservationRequest{
        Source:    "build-agent",
        Subject:   "user",
        Predicate: "prefers_language",
        Object:    "go",
        Tags:      []string{"preferences"},
    })

    // Ingest working memory state
    m.IngestWorkingState(ctx, ingestion.IngestWorkingStateRequest{
        Source:      "build-agent",
        ThreadID:    "session-001",
        State:       schema.TaskStateExecuting,
        NextActions: []string{"run tests", "deploy"},
    })

    // Retrieve with trust context
    resp, _ := m.Retrieve(ctx, &retrieval.RetrieveRequest{
        TaskDescriptor: "fix build error",
        Trust: &retrieval.TrustContext{
            MaxSensitivity: schema.SensitivityMedium,
            Authenticated:  true,
        },
        MemoryTypes: []schema.MemoryType{
            schema.MemoryTypeCompetence,
            schema.MemoryTypeSemantic,
        },
    })

    for _, r := range resp.Records {
        fmt.Printf("Found: %s (type=%s, confidence=%.2f)\n", r.ID, r.Type, r.Confidence)
    }
}

Build docs developers (and LLMs) love