Operate

Self-host & operations

Everything you need to run SyntheticMem yourself: a pgvector container, a handful of env keys, local ollama for embeddings, and an hourly prune.

Requirements

1. Postgres + pgvector (Docker)

The bundled docker-compose.yml brings up pgvector/pgvector:pg16 bound to a local-only port (127.0.0.1:5433) with a persistent volume. It reads the DB password from your .env and never touches a system database:

docker-compose.yml
services:
  postgres:
    image: pgvector/pgvector:pg16
    container_name: syntheticmem-pg
    environment:
      POSTGRES_USER: ${DB_USERNAME:-syntheticmem}
      POSTGRES_PASSWORD: ${DB_PASSWORD:?Set DB_PASSWORD in .env}
      POSTGRES_DB: ${DB_DATABASE:-syntheticmem}
    ports:
      - "127.0.0.1:5433:5432"
    volumes:
      - syntheticmem-pg-data:/var/lib/postgresql/data
      - ./docker/postgres/init:/docker-entrypoint-initdb.d:ro
bring it up
docker compose up -d        # first boot creates the `vector` extension + test DB
php artisan migrate         # agents, api_keys, memories (+ HNSW index)

2. Environment

.env
DB_CONNECTION=pgsql
DB_HOST=127.0.0.1
DB_PORT=5433
DB_DATABASE=syntheticmem
DB_USERNAME=syntheticmem
DB_PASSWORD=…                       # also used by docker compose

# MCP server
MCP_SERVER_NAME=syntheticmem
MCP_SERVER_VERSION=0.1.0
# MCP_ROUTE=api/mcp                 # the JSON-RPC path (default)
SYNTHETICMEM_MASTER_KEY=…           # shared key for /api/v1/keys provisioning
SYNTHETICMEM_MCP_SERVICE_AGENT=mcp-service

# Local ollama embeddings (768-dim, no auth)
OLLAMA_EMBED_URL=http://localhost:11434/api/embed
OLLAMA_EMBED_MODEL=nomic-embed-text

3. Mint & revoke agent keys

Each agent gets its own agt_… key (stored only as a SHA-256 hash; shown once). On the server:

php artisan
php artisan syntheticmem:mcp-token my-agent --name=laptop --namespace=my-project
php artisan syntheticmem:mcp-token --list
php artisan syntheticmem:mcp-token --revoke=<key-id>

Or over HTTP with the master key (see Getting started):

curl
curl -s -X POST https://your-host/api/v1/keys \
  -H "Authorization: Bearer $SYNTHETICMEM_MASTER_KEY" -H 'Content-Type: application/json' \
  -d '{"agent":"my-agent","label":"laptop"}'

curl -s -X POST https://your-host/api/v1/keys/revoke \
  -H "Authorization: Bearer $SYNTHETICMEM_MASTER_KEY" -H 'Content-Type: application/json' \
  -d '{"agent":"my-agent","label":"laptop"}'

4. The prune scheduler

Expired ephemeral memories (anonymous-24h, or any ttl_hours) are hard-deleted by memory:prune. It only touches rows whose expires_at is strictly in the past — permanent rows (expires_at IS NULL) can never match. It is wired into the Laravel scheduler to run hourly (withoutOverlapping()):

cron (once per host)
* * * * * cd /path/to/syntheticmem-app && php artisan schedule:run >> /dev/null 2>&1
manual / dry-run
php artisan memory:prune --dry-run     # report how many rows would go
php artisan memory:prune               # delete them

5. Deploy checklist