Skip to main content
By default, Charm uses local file-backed memory (runtime.memory.provider: local). On the cloud runner, workspace files are persisted under a managed workspace path (backed by cloud storage for serverless/daemon workloads). For Redis, PostgreSQL, or other backends, install a memory plugin or use the built-in supabase provider where configured.

Configuring a provider

runtime:
  adapter:
    type: langchain
    entry_point: src.main:agent
  memory:
    provider: redis
    config:
      url: "redis://localhost:6379/0"
The cloud runner sets CHARM_MEMORY_PROVIDER from this block so runtime code can resolve the active backend. Built-in provider names today:
ProviderSource
local (default)Core — file-backed
supabaseCore — when Supabase memory is configured
Custom namescharm.memory entry points

Underlying Checkpoint Mechanism

When using an external provider like supabase, Charm leverages thread identifiers (thread_id) and checkpoint namespaces to continuously sync state to the database. Upon waking up or migrating across devices, the runtime automatically queries the most recent state checkpoint. This architecture provides zero-downtime perception and seamless cross-device continuity without additional developer overhead.

Creating a memory plugin

1. Implement BaseMemoryStore

from typing import Any, Dict, List
import json
import redis
from charm.core.storage import BaseMemoryStore

class RedisMemory(BaseMemoryStore):
    def __init__(self, config: Dict[str, Any]):
        super().__init__(config)
        self.client = redis.Redis.from_url(
            self.config.get("url", "redis://localhost:6379/0")
        )

    def load_messages(self, thread_id: str) -> List[Dict[str, Any]]:
        raw = self.client.get(f"charm:memory:{thread_id}")
        return json.loads(raw) if raw else []

    def save_messages(self, thread_id: str, messages: List[Dict[str, Any]]) -> None:
        self.client.set(f"charm:memory:{thread_id}", json.dumps(messages))

    def get_langgraph_checkpointer(self) -> Any:
        return None

2. Register via entry points

[project.entry-points."charm.memory"]
redis = "charm_memory_redis.store:RedisMemory"

3. Use in charm.yaml

runtime:
  memory:
    provider: redis
    config:
      url: "${REDIS_URL}"

Using memory from agent code

import os
from charm.core.storage import StorageManager

def agent(inputs):
    thread_id = inputs.get("__charm_thread_id__", "default")
    provider = os.getenv("CHARM_MEMORY_PROVIDER", "local")

    store = StorageManager.get_provider(provider, {})
    history = store.load_messages(thread_id)

    history.append({"role": "user", "content": str(inputs)})
    store.save_messages(thread_id, history)

    return {"status": "success", "output": f"Turns: {len(history)}"}
StorageManager caches the active provider for the process. Pass the same config dict shape as runtime.memory.config when constructing plugins that need connection settings.

Cloud deployment

Unlike heavy adapters, Memory plugins are typically lightweight (e.g. redis, pymongo). This means the Charm Cloud Runner can install them dynamically at boot time without timing out. You do NOT need a custom_image to use most Memory Providers!
  1. Add the package to your agent’s requirements.txt:
charm-memory-redis
  1. Enable it in charm.yaml:
runtime:
  memory:
    provider: redis
    config:
      url: "${REDIS_URL}"
  1. Ensure policies.allow_internet_access (or network policy) allows reaching your database.
  2. Provide connection secrets via environment variables — not in committed charm.yaml.
If your memory plugin requires heavy native packages (e.g. psycopg2-binary takes longer), you should bake it into a runtime.custom_image instead. If the configured plugin is not found at runtime, Charm falls back to local file memory and logs a warning.

Share with the Community

Once your memory provider is working and published to PyPI, you can list it in the official Charm Store! Submit a Pull Request to the charm-community-plugin repository to add your package to memory/registry.json. Your integration will immediately appear on the Plugins page for other developers to discover.