{"slug": "commonplace-self-hosted-privacy-tiered-memory-for-your-ai-agents", "title": "Commonplace: Self-hosted, privacy-tiered memory for your AI agents", "summary": "Commonplace launches a self-hosted, privacy-tiered memory system for AI agents, using a two-tier Graphiti knowledge graph that runs entirely on local hardware by default, with a personal tier optionally using hosted models for non-confidential data and a client-confidential tier that never leaves the machine.", "body_md": "A self-hosted, two-tier [Graphiti](https://github.com/getzep/graphiti) knowledge graph that MCP\nclients (for example **Claude Code** and **Pi**) read from and write to over a private\n[Tailscale](https://tailscale.com) network. **It's offline-first: by default every part — including\nthe LLM that extracts your graph — runs on your own hardware, so nothing leaves the box.**\n\nIt runs on a single always-on Linux host with Docker and a consumer NVIDIA GPU. Your laptops and other devices are pure clients — they host nothing.\n\nKnowledge-graph ingestion uses an LLM to extract entities and relationships from text. That\nextraction is where your data would be exposed to a model — so by default `commonplace`\n\ndoes it\n**locally**, on your GPU, for both tiers. The two tiers split memory by confidentiality and by\nwhether you're allowed to trade locality for quality:\n\n| Tier | Graph | Extraction (default) | Where it runs | Use for |\n|---|---|---|---|---|\npersonal |\n`commonplace_personal` |\n`mistral:7b-instruct-q4_0` (local) |\nthe host's GPU | your own notes, projects, life — optionally a hosted model for quality |\nclient-confidential |\n`commonplace_client` |\n`mistral:7b-instruct-q4_0` (local) |\nthe host's GPU | confidential / NDA material that must never leave the machine |\n\nThe **personal** tier is local by default but may be pointed at a hosted model (e.g. Claude Haiku)\nfor higher-quality graphs on **non-confidential** data — opt in via `.env`\n\n(see *Hosted upgrade?*\nunder [Setup](#setup)). The **client** tier is always local; that's the whole point of it.\n\n**Retrieval is cheap and private on both tiers.** Search is embeddings + BM25 + graph traversal\nwith **no LLM in the query path**. The GPU only ever does slow, asynchronous *background*\nextraction — query latency is never affected. Slow local extraction is therefore fine.\n\nBoth tiers share **one embedder** (Ollama `nomic-embed-text`\n\n, 768-dim) and **one FalkorDB**\nholding two separate graphs, so the two memories stay isolated but the infrastructure stays simple.\n\n```\nflowchart TB\n    CC[\"Claude Code<br/>(client)\"]\n    PI[\"Pi<br/>(client)\"]\n    TS{{\"Tailscale<br/>MagicDNS · tailnet-only\"}}\n    ANT[\"Anthropic API<br/>Claude Haiku 4.5 · hosted\"]\n\n    CC --> TS\n    PI --> TS\n\n    subgraph HOST[\"your server — Docker\"]\n        direction TB\n        GW[\"<b>gateway</b> :8000 / :8001<br/>per-tier auth · logging · metrics\"]\n        MP[\"<b>mcp-personal</b><br/>personal tier · internal\"]\n        MC[\"<b>mcp-client</b><br/>client-confidential · internal\"]\n        GW --> MP\n        GW --> MC\n        OL[\"<b>Ollama</b> :11434<br/>nomic-embed-text · mistral:7b<br/>local GPU\"]\n        subgraph FALKOR[\"FalkorDB :6379 · browser UI :3000\"]\n            direction LR\n            GP[(\"commonplace_personal\")]\n            GC[(\"commonplace_client\")]\n        end\n        MP -->|store| GP\n        MC -->|store| GC\n        MP -. embed .-> OL\n        MC -. embed .-> OL\n        MC -->|extract · local| OL\n    end\n\n    TS -->|Bearer token| GW\n    MP -->|extract · hosted| ANT\n\n    classDef ext fill:#fff3e0,stroke:#e67e22,color:#111;\n    classDef tier fill:#e8f0fe,stroke:#4285f4,color:#111;\n    class ANT ext\n    class MP,MC tier\n```\n\n**One FalkorDB**, two graphs selected per-instance by`FALKORDB_DATABASE`\n\n(`commonplace_personal`\n\nvs`commonplace_client`\n\n).**Two Graphiti MCP instances**(`commonplace-mcp:local`\n\n, built from`zepai/knowledge-graph-mcp:standalone`\n\n— see`Dockerfile`\n\n), HTTP transport, served at path(trailing slash).`/mcp/`\n\n**One shared Ollama embedder**(`nomic-embed-text`\n\n, 768-dim) used by*both*instances. Do not mix embedders — vectors from different embedders are not comparable.**A gateway**(Caddy) fronts both tiers: it owns the host ports, requires a** per-tier bearer token**(so a client with only the client token can't reach the personal tier), and emits access logs (audit) + Prometheus metrics. The MCP containers themselves are internal-only.\n\nReplace\n\n`your-server.your-tailnet.ts.net`\n\nwith your host's Tailscale MagicDNS name throughout (run`tailscale status`\n\non the host to find it).\n\n| Tier | Host endpoint (tailnet) | Internal port | Graph (`FALKORDB_DATABASE` ) |\nLLM | `SEMAPHORE_LIMIT` |\n|---|---|---|---|---|---|\n| personal | `http://your-server.your-tailnet.ts.net:8000/mcp/` |\n8000 | `commonplace_personal` |\n`mistral:7b…` (local, default) |\n1 |\n| client | `http://your-server.your-tailnet.ts.net:8001/mcp/` |\n8000 | `commonplace_client` |\n`mistral:7b-instruct-q4_0` |\n1 |\n| FalkorDB | `127.0.0.1:6379` (host-local only) |\n6379 | both graphs | — | — |\n| FalkorDB UI | `http://your-server.your-tailnet.ts.net:3000` |\n3000 | browse either graph | — | — |\n| Metrics | `127.0.0.1:9180/metrics` (host-local only) |\n9180 | gateway (Prometheus) | — | — |\n\nThe personal/client endpoints require `Authorization: Bearer <tier-token>`\n\n(set `PERSONAL_TOKEN`\n\n/\n`CLIENT_TOKEN`\n\nin `.env`\n\n). A request without the right token gets `401`\n\n.\n\nOn the **host**:\n\n**Docker** with Compose v2.running on the host, serving the shared embedder and the local extraction model. The MCP containers reach it over HTTP — the GPU is used by Ollama, not by the containers, so no GPU passthrough into Docker is required. A consumer NVIDIA GPU with ~8 GB VRAM runs[Ollama](https://ollama.com)`mistral:7b-instruct-q4_0`\n\ncomfortably; CPU-only works but local extraction is slow.— the MCP endpoints are served over the tailnet, not the public internet.[Tailscale](https://tailscale.com)**No API keys required.** Both tiers extract locally by default. An**Anthropic API key** is needed*only*if you opt the personal tier into a hosted model (see*Hosted upgrade?*below).\n\nOn each **client** (laptop, etc.): Tailscale, plus an MCP-capable client (Claude Code, Pi, …).\n\nRun on the host, from a clone of this repo (e.g. `~/commonplace`\n\n):\n\n```\n# 1. Pull the models Ollama will serve\nollama pull nomic-embed-text\nollama pull mistral:7b-instruct-q4_0\n\n# 2. Configure secrets\ncp .env.example .env\n#    edit .env and set:\n#      FALKORDB_PASSWORD          (openssl rand -hex 24)\n#      PERSONAL_TOKEN / CLIENT_TOKEN   gateway bearer tokens (openssl rand -hex 32 each)\n#    (no ANTHROPIC_API_KEY needed — extraction is local by default)\n\n# 3. Build the local image and start the stack\ndocker compose up -d\ndocker compose ps        # all services should report healthy\n```\n\nThen point a client at the two endpoints — see [Client configuration](#client-configuration).\n\nHosted upgrade?Everything is local by default. To point thepersonaltier at a hosted model for higher-quality graphs (non-confidential data only), set in`.env`\n\n:`PERSONAL_LLM_PROVIDER=anthropic`\n\n,`PERSONAL_LLM_MODEL=claude-haiku-4-5`\n\n,`PERSONAL_SEMAPHORE_LIMIT=5`\n\n, and`ANTHROPIC_API_KEY=…`\n\n. The client tier stays local regardless.\n\nUpgrading from a pre-gateway deploy?Add`PERSONAL_TOKEN`\n\n/`CLIENT_TOKEN`\n\nto`.env`\n\n, then`docker compose up -d --build --force-recreate`\n\n(the MCP tiers move behind the gateway and the ontology change needs a recreate). Re-add each client with its`Authorization: Bearer`\n\nheader — existing token-less clients will start getting`401`\n\n.\n\nThese are the landmines specific to the current (2026) Graphiti MCP server. Several contradict older docs.\n\n**There is no** To use Ollama you set`openai_generic`\n\nprovider string.`provider: \"openai\"`\n\nand point`api_url`\n\nat a non-OpenAI URL; the server then auto-selects its`OpenAIGenericClient`\n\ninternally. That generic client is what avoids OpenAI's beta`responses.parse()`\n\n(which Ollama does not implement). Setting`provider: \"openai_generic\"`\n\nis invalid.**There is no** The MCP server has a single`small_model`\n\nsetting.`llm.model`\n\n. On the openai path it uses that same model for the \"small\" slot too. The infamous`gpt-4.1-mini`\n\nis only a fallback used when`model`\n\nis`None`\n\n— pinning`llm.model`\n\nis enough to never hit it., and`json_schema`\n\nstructured output is always on for the local path and cannot be disabled— retries are built-in (tenacity, 4 attempts). There is no config knob for either. If a small local model produces invalid JSON, the only lever is a more capable model.`instructor`\n\nis not used there**Ollama must be reachable from inside the containers.** Ollama runs on the*host*, so each MCP service needs`extra_hosts: [\"host.docker.internal:host-gateway\"]`\n\nand an`api_url`\n\nof`http://host.docker.internal:11434/v1`\n\n. Ollama must listen on`0.0.0.0:11434`\n\n(it does by default).Two graphs in one FalkorDB = two instances with the same`FALKORDB_DATABASE`\n\nselects the graph;`group_id`\n\ndoes not.`FALKORDB_URI`\n\nand different`FALKORDB_DATABASE`\n\n.`group_id`\n\nonly namespaces nodes*within*a graph.**FalkorDB host/port are parsed from**—`FALKORDB_URI`\n\n`FALKORDB_HOST`\n\n/`FALKORDB_PORT`\n\nare ignored. The only env overrides read are`FALKORDB_URI`\n\nand`FALKORDB_PASSWORD`\n\n.**FalkorDB password is set via**, an env var —`REDIS_ARGS=--requirepass …`\n\n*not*by overriding the container`command`\n\n(that would stop the FalkorDB module from loading).**Use the**`:standalone`\n\nimage, not`:latest`\n\n.`zepai/knowledge-graph-mcp:latest`\n\nbundles its own FalkorDB;`:standalone`\n\nexpects an external one — required to share a single FalkorDB across two instances.**The MCP path has a trailing slash:**(FastMCP default; not configurable).`/mcp/`\n\n**Anthropic model id: use the bare alias** The`claude-haiku-4-5`\n\n, not`claude-haiku-4-5-latest`\n\n.`-latest`\n\nsuffix is an OpenAI-ism; the Anthropic API 404s on it (`not_found_error: model`\n\n). The bare alias resolves to the current dated snapshot (`claude-haiku-4-5-20251001`\n\n).**The Anthropic provider needs an explicit numeric** graphiti passes`llm.temperature`\n\n.`temperature=config.temperature`\n\n; with none set it sends`null`\n\nand the API 400s (`temperature: Input should be a valid number`\n\n), so every personal-tier episode queues but never processes. The OpenAI/Ollama generic client tolerates`null`\n\n, so this bites only the Anthropic tier. Set e.g.`temperature: 0.0`\n\n.**The**`:standalone`\n\nimage ships WITHOUT the`anthropic`\n\nSDK.`provider: anthropic`\n\nthen fails at startup — \"Anthropic client not available in current graphiti-core version\" (the factory's`HAS_ANTHROPIC`\n\nis False because`import anthropic`\n\nraises). The bundled`Dockerfile`\n\nadds it (`uv pip install anthropic`\n\n).**graphiti-core builds a default OpenAI reranker at init** that demands`OPENAI_API_KEY`\n\neven though the search path uses`NODE_HYBRID_SEARCH_RRF`\n\n(no cross-encoder). Give each tier a dummy`OPENAI_API_KEY`\n\nso it can construct; point`OPENAI_BASE_URL`\n\nat Ollama so even an accidental call stays on-box. In practice it is never called.**FastMCP rejects non-localhost Host headers with HTTP 421 \"Invalid Host header\".** It auto-enables DNS-rebinding protection with a localhost-only allow-list at construction and passes that object explicitly into its pydantic Settings, so the`FASTMCP_…`\n\nenv vars cannot override it (init kwargs beat env). The bundled`patch_transport_security.py`\n\n(run in the Dockerfile) disables the protection — safe on a tailnet, where the network is the trust boundary and clients are agents, not browsers. To tighten, set explicit`allowed_hosts`\n\ninstead.**The container env var for the OpenAI-compatible base URL is**(graphiti's config expansion), not`OPENAI_API_URL`\n\n`OPENAI_BASE_URL`\n\n. Note the reranker (#13) is the opposite — it reads the OpenAI SDK's`OPENAI_BASE_URL`\n\n. Two different names for two different clients.\n\nRun on the host, from the repo directory (e.g. `~/commonplace`\n\n).\n\n**Redeploy in one command** — `scripts/commonplace`\n\nwraps the pull → rebuild → recreate flow\n(symlink it onto your `PATH`\n\n, e.g. `ln -sf \"$PWD/scripts/commonplace\" ~/.local/bin/commonplace`\n\n):\n\n```\ncommonplace update           # sync repo, rebuild image, recreate config-sensitive services\ncommonplace update --reset   # same, but hard-reset to origin/main (after a force-push)\ncommonplace status           # service health + graph counts\n```\n\nThe underlying compose commands, if you'd rather run them by hand:\n\n```\n# Bring the stack up (after .env is filled in)\ndocker compose up -d\n\n# Status / health\ndocker compose ps\ndocker compose logs -f mcp-personal     # or mcp-client, falkordb\n\n# Restart one instance after a config change (config/*.yaml does not hot-reload)\ndocker compose up -d --force-recreate mcp-client\n\n# Rebuild the local image after editing the Dockerfile or patch_transport_security.py\ndocker compose up -d --build\n\n# Stop / start (data persists in the falkordb_data volume)\ndocker compose stop\ndocker compose start\n\n# Tear down (KEEP data)\ndocker compose down\n# Tear down AND delete the graphs\ndocker compose down -v\n```\n\nQuick MCP health check (from a client, over the tailnet or LAN). Without a token you get `401`\n\n(auth working); with the right tier token you get `307`\n\n:\n\n```\ncurl -s -o /dev/null -w \"%{http_code}\\n\" -H \"Authorization: Bearer $PERSONAL_TOKEN\" \\\n  http://your-server.your-tailnet.ts.net:8000/mcp/\ncurl -s -o /dev/null -w \"%{http_code}\\n\" -H \"Authorization: Bearer $CLIENT_TOKEN\" \\\n  http://your-server.your-tailnet.ts.net:8001/mcp/\n\n# Is anyone actually using it? (run on the host)\n./scripts/graph_stats.sh        # writes landing per tier\n./scripts/mcp_activity.sh       # reads/writes per tier from the gateway log\n```\n\nFalkorDB persists to the `falkordb_data`\n\nvolume — mounted at its actual data dir\n(`/var/lib/falkordb/data`\n\n), with **AOF enabled** (`--appendonly yes`\n\n), so writes are durable to ~1s\nand survive container recreates. Back up / restore the whole data dir (RDB + AOF) with the scripts:\n\n``` php\n./scripts/backup.sh                                       # -> ./backups/falkordb-<stamp>.tar.gz\n./scripts/restore.sh ./backups/falkordb-<stamp>.tar.gz    # overwrites live data (prompts to confirm)\n```\n\nBoth read `FALKORDB_PASSWORD`\n\nfrom `.env`\n\n. `backup.sh`\n\nasks the server for its data dir, so it keeps\nworking even if the path changes.\n\nEarlier revisions mounted the volume at\n\n`/data`\n\nwhile FalkorDB wrote to`/var/lib/falkordb/data`\n\non the ephemeral container layer — so data was lost on every`--force-recreate`\n\n. The mount path is now fixed; redeploy with`commonplace update`\n\nto apply it.\n\n**Default: MagicDNS + port.** The gateway binds`:8000`\n\n/`:8001`\n\non the host and is reached over the tailnet at`http://your-server.your-tailnet.ts.net:8000/mcp/`\n\nand`:8001/mcp/`\n\n. This is tailnet-reachable (and LAN-reachable) but**not** public — do not port-forward these on your router.**Auth.** Every request needs`Authorization: Bearer <tier-token>`\n\n; the gateway 401s otherwise. Separate`PERSONAL_TOKEN`\n\n/`CLIENT_TOKEN`\n\ngive each client only the tiers it should touch.**FalkorDB**(host-local) — never on the tailnet.`:6379`\n\nand metrics`:9180`\n\nbind to`127.0.0.1`\n\nonly**Keep the host single-homed.** The host's primary interface should hold exactly one IPv4. If a second address appears (e.g. a static IP*plus*a DHCP lease), Tailscale can advertise two WireGuard endpoints and the tunnel flaps, which**black-holes TCP over MagicDNS while the LAN and**(disco pings roam across endpoints; real TCP does not). On Ubuntu this most often comes from cloud-init re-enabling DHCP — disable its network management (`tailscale ping`\n\nstill appear to work`echo 'network: {config: disabled}' | sudo tee /etc/cloud/cloud.cfg.d/99-disable-network-config.cfg`\n\n). Symptom to watch for:`ip -brief addr show <iface>`\n\nlisting more than one address on your LAN subnet.**HTTPS upgrade (optional).** To serve the MCP endpoints as tailnet-only HTTPS names instead of raw ports:then point clients at\n\n```\ntailscale serve --bg --https=8443 http://localhost:8000   # personal\ntailscale serve --bg --https=8444 http://localhost:8001   # client\n```\n\n`https://your-server.your-tailnet.ts.net:8443/mcp/`\n\netc. MagicDNS:port is the simpler default and is what the client config below uses.\n\nReplace\n\n`your-server.your-tailnet.ts.net`\n\nwith your host's Tailscale MagicDNS name (`tailscale status`\n\n). The identical ports/paths are also served on the host's LAN IP, which is a handy fallback if MagicDNS is ever unreachable.\n\nPass the per-tier bearer token with `--header`\n\n. Give a client only the tiers it should reach (e.g.\nomit the personal server on a machine that handles confidential work):\n\n```\nclaude mcp add --scope user --transport http commonplace-personal http://your-server.your-tailnet.ts.net:8000/mcp/ \\\n  --header \"Authorization: Bearer $PERSONAL_TOKEN\"\nclaude mcp add --scope user --transport http commonplace-client   http://your-server.your-tailnet.ts.net:8001/mcp/ \\\n  --header \"Authorization: Bearer $CLIENT_TOKEN\"\nclaude mcp list   # both should report ✓ Connected\n```\n\n(New servers load on the next Claude Code start.)\n\nPi has no native MCP — add the community bridge, then a global `mcp.json`\n\n:\n\n```\npi install npm:@spences10/pi-mcp     # records the bridge in settings.json\n```\n\nEach server entry must include `\"type\": \"http\"`\n\n; a `url`\n\n-only entry triggers an OAuth handshake\nthis server doesn't support. The extension lazy-connects by default — set\n`MY_PI_MCP_EAGER_CONNECT=1`\n\nto connect and discover tools at startup.\n\n```\n{\n  \"mcpServers\": {\n    \"commonplace-personal\": {\n      \"type\": \"http\",\n      \"url\": \"http://your-server.your-tailnet.ts.net:8000/mcp/\",\n      \"headers\": { \"Authorization\": \"Bearer YOUR_PERSONAL_TOKEN\" }\n    },\n    \"commonplace-client\": {\n      \"type\": \"http\",\n      \"url\": \"http://your-server.your-tailnet.ts.net:8001/mcp/\",\n      \"headers\": { \"Authorization\": \"Bearer YOUR_CLIENT_TOKEN\" }\n    }\n  }\n}\n```\n\nAny device on the tailnet can use the same two endpoints — there is nothing per-client on the server. To add one:\n\n- Join the device to the tailnet (\n`tailscale up`\n\n) and confirm it can reach the host (`tailscale ping your-server`\n\n). - For Claude Code, run the two\n`claude mcp add … /mcp/`\n\ncommands above (user scope). - For any MCP client, add both servers with\n`\"type\": \"http\"`\n\npointing at`:8000/mcp/`\n\nand`:8001/mcp/`\n\n. - Nothing to change on the host — graphs and auth are shared; reads/writes from the new client land in the same two graphs.\n- For HTTPS, expose via\n`tailscale serve`\n\n(above) and use the`https://…`\n\nURLs instead.\n\nTwo things turn this from a memory *store* into a memory *system agents use well*:\n\n**Per-tier ontology.** Each tier defines`graphiti.entity_types`\n\nin its config (personal: Preference, Project, Person, Decision, …; client: Engagement, Stakeholder, Requirement, Risk, …). These type descriptions constrain extraction — the single biggest lever on graph quality, and they help the weak local model the most.**An agent protocol.** is the contract for any client (Claude Code, Pi): search before answering, write durable facts,`docs/memory-protocol.md`\n\n**never cross tiers**(no confidential data on the hosted personal tier), and cite what you used. Install it as a skill or system prompt — without it, agents rarely call memory and the graph stays empty.\n\n**Is it actually being used?** `scripts/graph_stats.sh`\n\nshows whether *writes* are landing;\n`scripts/mcp_activity.sh`\n\n(and the Prometheus endpoint on `:9180`\n\n) show whether agents are *reading*.\nSeed an existing corpus with `scripts/ingest_markdown.py`\n\n, pull token-budgeted context with\n`scripts/recall.py`\n\n, gate retrieval quality with `eval/run_eval.py`\n\n, and review resolved\ncontradictions with `scripts/contradictions.sh`\n\n. See [ docs/ROADMAP.md](/itsmeduncan/commonplace/blob/main/docs/ROADMAP.md) for what's\nshipped vs. still open (a local reranker remains the notable deferral).\n\n```\ncommonplace/\n├── docker-compose.yml           # FalkorDB + 2 MCP instances + gateway, restart: unless-stopped\n├── Dockerfile                   # commonplace-mcp:local — standalone image (digest-pinned) + patch\n├── patch_transport_security.py  # build-time: allow remote Host headers (disable DNS-rebind guard)\n├── gateway/\n│   └── Caddyfile                # per-tier bearer auth + access logging + Prometheus metrics\n├── config/\n│   ├── personal.yaml            # instance A — Anthropic Haiku extraction + personal ontology\n│   └── client.yaml              # instance B — local Ollama extraction + confidential ontology\n├── scripts/\n│   ├── commonplace              # operate CLI: `commonplace update` redeploys the stack\n│   ├── graph_stats.sh           # write counts per tier   · mcp_activity.sh  # read counts (gateway log)\n│   ├── recall.py                # token-budgeted recall    · contradictions.sh # superseded facts\n│   ├── backup.sh / restore.sh   # FalkorDB dump + restore\n│   └── ingest_markdown.py       # load a markdown corpus (notes/docs) into a tier\n├── eval/\n│   ├── queries.yaml             # retrieval eval cases (question → expected facts)\n│   └── run_eval.py              # scores recall against a tier\n├── docs/\n│   ├── memory-protocol.md       # how agents should read/write memory (tier safety, cite-back)\n│   └── ROADMAP.md               # hardening & maturity plan\n├── .env.example                 # template; copy to .env on the host (gitignored)\n├── .dockerignore                # keeps .env and other secrets out of the build context\n├── CLAUDE.md                    # guidance for Claude Code working in this repo\n├── LICENSE                      # MIT\n└── README.md\n```\n\nSecrets live only in `.env`\n\non the host and are never committed. The repo is the source of\ntruth: edit a clone, push to your fork, `git pull`\n\non the host, `docker compose up -d`\n\n.\n\n[MIT](/itsmeduncan/commonplace/blob/main/LICENSE).", "url": "https://wpnews.pro/news/commonplace-self-hosted-privacy-tiered-memory-for-your-ai-agents", "canonical_source": "https://github.com/itsmeduncan/commonplace", "published_at": "2026-06-30 22:46:21+00:00", "updated_at": "2026-06-30 23:20:03.728381+00:00", "lang": "en", "topics": ["ai-agents", "ai-infrastructure", "ai-tools", "ai-safety", "ai-ethics"], "entities": ["Commonplace", "Graphiti", "Claude Code", "Pi", "Tailscale", "FalkorDB", "Ollama", "Mistral"], "alternates": {"html": "https://wpnews.pro/news/commonplace-self-hosted-privacy-tiered-memory-for-your-ai-agents", "markdown": "https://wpnews.pro/news/commonplace-self-hosted-privacy-tiered-memory-for-your-ai-agents.md", "text": "https://wpnews.pro/news/commonplace-self-hosted-privacy-tiered-memory-for-your-ai-agents.txt", "jsonld": "https://wpnews.pro/news/commonplace-self-hosted-privacy-tiered-memory-for-your-ai-agents.jsonld"}}