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
- PHP 8.4 + Composer, a web server running the Laravel app.
- Docker (for the Postgres + pgvector container).
- A local ollama with
nomic-embed-textpulled (ollama pull nomic-embed-text).
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:
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
docker compose up -d # first boot creates the `vector` extension + test DB php artisan migrate # agents, api_keys, memories (+ HNSW index)
2. Environment
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 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 -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()):
* * * * * cd /path/to/syntheticmem-app && php artisan schedule:run >> /dev/null 2>&1
php artisan memory:prune --dry-run # report how many rows would go php artisan memory:prune # delete them
5. Deploy checklist
composer install --no-dev,npm ci && npm run build(compiles the landing + docs CSS and self-hosted fonts).php artisan migrate --force, thenconfig:cache/route:cache/view:cache.- Make sure
public/openapi.jsonis deployed — the Swagger UI loads it directly. - Reload PHP-FPM so the new code and cached config take effect.