memgate
by Denizayhan04
README.md
# Memgate
**Access control, conflict resolution, and audit for shared agent memory.**
Memgate sits in front of the memory your AI agents share. It decides who can read and write what, resolves conflicting writes, and records every operation — so a multi-agent system's memory stays trustworthy as it grows.
It speaks [MCP](https://modelcontextprotocol.io), so agents connect with one config entry and no code changes.
> Status: early but working. The core (policy, conflict handling, audit, MCP tools) runs and is tested end-to-end with real agents. Adapters for external memory backends and a hosted dashboard are on the roadmap, not built yet. Feedback and issues welcome.
---
## Demo
<!--
Record a short terminal session showing: agent creation, an agent writing
and reading, a denied write, and the audit log. Then replace the line below.
brew install asciinema # or: pipx install asciinema
asciinema rec memgate-demo.cast
# run the flow, then Ctrl+D
asciinema upload memgate-demo.cast
Paste the badge asciinema gives you here, e.g.:
[](https://asciinema.org/a/XXXXXX)
-->
[](https://asciinema.org/a/gV89C4wOep9phIfV)
---
## The problem
A single agent's memory is easy: keep the conversation in context, done. But when several agents share one memory pool — one handling support, one writing code, one doing billing — that pool degrades:
- **Conflicting writes.** One agent records "customer is happy," another records "customer is angry." Nobody knows which is current.
- **No access boundaries.** The billing agent can read customer PII; the support agent can overwrite financial records. Nothing stops it.
- **No accountability.** When something goes wrong, there's no record of which agent wrote what, or when.
Memory stores like pgvector, Mem0, or Zep *hold* memory well. They don't *govern* it. Memgate is the governance layer that sits on top.
## The idea
Think of the shared memory as a ledger, and Memgate as the gatekeeper standing in front of it. Agents never touch the ledger directly — every request goes through the gate, which does three things:
1. **Policy** — checks whether this agent's role may read or write this namespace. Default is deny.
2. **Conflict resolution** — when a record already exists for an entity, applies the namespace's strategy (`last-write-wins`, `versioned`, or `require-review`) instead of silently clobbering it.
3. **Audit** — appends every operation, allowed or denied, to an append-only log.
Identity is bound to the API key, not to anything the agent says. An agent cannot escalate its own privileges by claiming a different role.
## How it works
```
Agent A ─┐
Agent B ─┼── MCP ──▶ [ Memgate ] ──▶ Postgres + pgvector
Agent C ─┘ policy · conflict · audit
```
Agents call three tools through MCP:
| Tool | What it does |
|------|--------------|
| `memory_write(namespace, entity_key, content)` | Policy check → conflict resolution → write → audit |
| `memory_read(namespace, entity_key)` | Policy check → return the active record → audit |
| `memory_search(namespace, query)` | Policy check → semantic search over authorized namespaces → audit |
---
## Quickstart
Requires Docker.
```bash
git clone https://github.com/denizayhan04/memgate
cd memgate
docker compose up --build
```
This starts Postgres (with pgvector), runs migrations, and serves the MCP endpoint on **:8088**.
Create an agent and get its API key:
```bash
docker compose exec memgate memgate agent create --name support-bot --role support
# → API key (shown once): mg_live_...
```
Connect an MCP client (Claude Code, Cursor, or anything that speaks MCP). Add to your project's `.mcp.json`:
```json
{
"mcpServers": {
"memgate": {
"type": "http",
"url": "http://localhost:8088/mcp",
"headers": { "Authorization": "Bearer mg_live_YOUR_KEY" }
}
}
}
```
Now the agent has `memory_write`, `memory_read`, and `memory_search` tools, scoped to whatever its role allows.
Inspect what happened:
```bash
docker compose exec memgate memgate audit --entity "customer:acme"
```
```
ID AT AGENT ACTION NAMESPACE ENTITY_KEY RESULT
14 2026-07-08T15:43:30Z test-eng write customer-data customer:acme denied
13 2026-07-08T15:43:14Z test-eng read customer-data customer:acme allowed
12 2026-07-08T15:41:05Z test-support read customer-data customer:acme allowed
11 2026-07-08T15:41:02Z test-support write customer-data customer:acme allowed
```
Two agents, one shared record, different permissions — the support agent writes and reads; the engineer reads but cannot write. Every attempt, allowed or denied, is on record.
---
## Policies
Policies live in a YAML file (`config/policies.example.yaml` by default), not in the database — so they're versioned in git and reviewed like any other config. The rule is **default deny**: anything not explicitly granted is closed.
```yaml
namespaces:
customer-data:
conflict_strategy: versioned # old record superseded, new active; history queryable
tech-context:
conflict_strategy: last-write-wins # new record overwrites old
billing:
conflict_strategy: require-review # write parked as pending until a human approves
roles:
support:
customer-data: [read, write]
tech-context: [read]
engineer:
tech-context: [read, write]
customer-data: [read]
finance-bot:
billing: [read, write]
customer-data: [read]
auditor:
"*": [read]
```
The running server reloads the policy file without a restart, on `SIGHUP` or via `memgate reload`.
### Conflict strategies
When a write targets an entity that already has an active record:
- **`last-write-wins`** — the new record becomes active, the old one is marked superseded.
- **`versioned`** — same, but history is preserved and queryable (what did we know, and when).
- **`require-review`** — the new write is parked as `pending_review`; the existing record stays active until a human approves it.
Reviewing parked writes:
```bash
memgate review list # list memories awaiting review
memgate review approve <id> # make a pending memory active
memgate review reject <id> # reject it
```
---
## CLI
```
memgate serve Start the MCP (Streamable HTTP) server
memgate migrate Apply SQL migrations
memgate agent create --name N --role R Create an agent and print its API key
memgate review list List memories awaiting review
memgate review approve <id> Approve a pending memory
memgate review reject <id> Reject a pending memory
memgate reload Reload the policy YAML in a running server
memgate audit [--entity K] [--namespace N] [--agent A] [--limit N]
Show audit log, most recent first
```
### Environment
| Variable | Purpose |
|----------|---------|
| `MEMGATE_DATABASE_URL` | Postgres DSN (required) |
| `MEMGATE_POLICY_FILE` | Policy YAML path (default `config/policies.example.yaml`) |
| `MEMGATE_LISTEN_ADDR` | Listen address for `serve` (default `:8080`) |
| `MEMGATE_MIGRATIONS_DIR` | Migrations directory (default `migrations`) |
| `MEMGATE_PID_FILE` | PID file written by `serve`, read by `reload` |
| `MEMGATE_OPENAI_API_KEY` | Optional. Set for real embeddings; unset falls back to a deterministic offline embedder, so search works without a key. |
---
## Local development
```bash
make build # compile to bin/memgate
make test # run tests
make migrate # apply migrations (needs MEMGATE_DATABASE_URL)
make run # run the MCP server locally
make up # docker compose up --build
make down # stop and remove services
```
Running from source without Docker:
```bash
export MEMGATE_DATABASE_URL="postgres://memgate:memgate@localhost:5432/memgate?sslmode=disable"
go run ./cmd/memgate agent create --name support-bot --role support
```
---
## Design notes
- **Backend-agnostic by design.** The memory store is behind an interface. Today the only implementation is Postgres + pgvector; adapters for external stores (Mem0, Zep) can be added without touching the policy, conflict, or audit layers.
- **Identity is the key, not the claim.** An agent's role is resolved from its API key on every request. What the agent *says* about its role is irrelevant — there is no way to self-escalate.
- **Audit is append-only.** The audit log rejects updates and deletes at the database level, not just in application code.
## Roadmap
- Backend adapters (Mem0, Zep)
- REST endpoints alongside MCP, for clients that don't speak MCP
- A read-only audit dashboard
- Team policy management and SSO (hosted)
## Tech
Go · Postgres + pgvector · [mark3labs/mcp-go](https://github.com/mark3labs/mcp-go) · MCP Streamable HTTP transport.
## License
Apache 2.0. See [LICENSE](LICENSE).
This server cannot be deployed
Maintenance
ActivitySlowing
ResponsivenessNo issues