feat: optional shared/external MemPalace via MEMPALACE_REMOTE_URL
Lint workflows / actionlint (push) Successful in 21s

Wire the shared-palace option (implemented in mempalace-toolkit's mempalace.ts)
into the container:
- .env.example: document MEMPALACE_REMOTE_URL / MEMPALACE_REMOTE_TOKEN (env_file-only,
  per this repo's convention).
- docker-compose.mempalace.yml: optional shared server (mempalace-mcp --transport http),
  loopback-bound by default.
- docker-compose.yml: local-vs-external note on the palace-volume comment.
- README + CHANGELOG (Unreleased).
This commit is contained in:
pi
2026-07-02 13:09:29 +02:00
parent 3a59e15563
commit d73bf2e9d3
5 changed files with 115 additions and 2 deletions
+13
View File
@@ -9,6 +9,19 @@ WORKSPACE_PATH=~/projects
# Path to SSH keys on host # Path to SSH keys on host
SSH_KEY_PATH=~/.ssh SSH_KEY_PATH=~/.ssh
# ── MemPalace memory (local by default) ───────────────────────────
# By default the mempalace.ts extension spawns a LOCAL mempalace-mcp stdio
# server (palace at ~/.mempalace). Uncomment the devbox-palace volume in
# docker-compose.yml to persist it across container recreation.
#
# To instead share ONE MemPalace across containers/harnesses (pi + opencode
# + native), set the URL below. When set, the extension connects over HTTP
# and NO local mempalace-mcp is spawned; the devbox-palace volume is then
# irrelevant. MEMPALACE_REMOTE_TOKEN, if set, is sent as a bearer token.
# Serve it with: mempalace-mcp --transport http --host 0.0.0.0 --port 8765
# MEMPALACE_REMOTE_URL=http://mempalace.lan:8765/mcp
# MEMPALACE_REMOTE_TOKEN=
# ── LAN access from the container (host-OS-agnostic) ───────────────── # ── LAN access from the container (host-OS-agnostic) ─────────────────
# On VM-backed hosts (macOS OrbStack / Docker Desktop) the container can't # On VM-backed hosts (macOS OrbStack / Docker Desktop) the container can't
# reach the host's directly-attached LAN peers by default. The entrypoint # reach the host's directly-attached LAN peers by default. The entrypoint
+10
View File
@@ -15,6 +15,16 @@ Pre-v1.0.0 tags followed the pi npm version (`v{pi_version}[letter]`).
### Added ### Added
- **Share one MemPalace across containers via `MEMPALACE_REMOTE_URL`.** The
`mempalace.ts` bridge (from `mempalace-toolkit`) can now connect to a shared
MemPalace over HTTP instead of spawning a per-container local server: set
`MEMPALACE_REMOTE_URL=http://<host>:8765/mcp` (optionally
`MEMPALACE_REMOTE_TOKEN`) in `.env` and no local `mempalace-mcp` is spawned.
A new `docker-compose.mempalace.yml` stands up such a shared server
(`mempalace-mcp --transport http`). Leaving the URL unset keeps the default
local-per-container palace. See `.env.example`. (The HTTP transport is
unauthenticated — keep it on a trusted network or behind a reverse proxy.)
- **Two non-modal terminal editors alongside `nvim`: `nano` and `micro`.** - **Two non-modal terminal editors alongside `nvim`: `nano` and `micro`.**
The image previously shipped only `nvim` (with `EDITOR=nvim`), a modal The image previously shipped only `nvim` (with `EDITOR=nvim`), a modal
vi-style editor. Not everyone is comfortable with vi keybindings, so both vi-style editor. Not everyone is comfortable with vi keybindings, so both
+4 -1
View File
@@ -29,7 +29,10 @@ on the host.
- ChromaDB embedding model pre-warmed at build time (`all-MiniLM-L6-v2`) - ChromaDB embedding model pre-warmed at build time (`all-MiniLM-L6-v2`)
The host-mounted palace at `~/.mempalace` is shared across the host and The host-mounted palace at `~/.mempalace` is shared across the host and
this container so all your agents share one brain. this container so all your agents share one brain. To instead share a palace
across *several* containers/harnesses, set `MEMPALACE_REMOTE_URL` to a shared
MemPalace HTTP endpoint (see `.env.example` and `docker-compose.mempalace.yml`);
the bridge then connects over HTTP and spawns no local server.
### Modern CLI tooling ### Modern CLI tooling
+84
View File
@@ -0,0 +1,84 @@
# Shared MemPalace server (optional) — one palace for many clients.
#
# Runs `mempalace-mcp` over HTTP so several containers/harnesses (pi +
# opencode + native) can share ONE palace instead of each keeping its own.
# Point every client at it by setting, in that client's .env:
#
# MEMPALACE_REMOTE_URL=http://<reachable-host>:8765/mcp
#
# (see .env.example). When set, the client connects over HTTP and does NOT
# spawn its own local mempalace-mcp.
#
# Start: docker compose -f docker-compose.mempalace.yml up -d
# Stop: docker compose -f docker-compose.mempalace.yml down
# Logs: docker compose -f docker-compose.mempalace.yml logs -f
#
# Why reuse the devbox image? mempalace-mcp is already installed in it, and
# reusing it GUARANTEES the server's mempalace version matches the clients'
# (both are pinned by the same image build). Override with a slimmer image via
# MEMPALACE_SERVER_IMAGE if you prefer (it must provide `mempalace-mcp`).
#
# ⚠ SECURITY: mempalace-mcp's HTTP transport has NO authentication of its own.
# Do NOT expose port 8765 to an untrusted network. The default below binds to
# 127.0.0.1 (host loopback) only. To let sibling containers reach it, either
# attach them to the shared `mempalace-net` network (container-to-container, no
# host port needed — use http://mempalace-server:8765/mcp), or front it with a
# reverse proxy that enforces MEMPALACE_REMOTE_TOKEN as `Authorization: Bearer`.
name: mempalace-server
services:
mempalace:
image: ${MEMPALACE_SERVER_IMAGE:-joakimp/pi-devbox:latest}
container_name: mempalace-server
# Bypass the devbox entrypoint (dev-shell/LAN/config setup) and run the
# HTTP MCP server directly. HOME + explicit --palace pin the data path so
# it does not depend on the image's default user/HOME. Runs as root so it
# can initialise the fresh named volume; the volume is dedicated to this
# server (clients reach it over HTTP, never by mounting it).
entrypoint: []
user: "0:0"
environment:
- HOME=/data
command:
- mempalace-mcp
- --transport
- http
- --host
- "0.0.0.0"
- --port
- "8765"
- --palace
- /data/.mempalace
restart: unless-stopped
# Loopback-only by default (see SECURITY note). Use "8765:8765" to expose on
# all host interfaces, or drop `ports:` entirely and rely on mempalace-net.
ports:
- "127.0.0.1:8765:8765"
volumes:
# The shared palace data — precious; back this up.
- mempalace-shared:/data/.mempalace
# Embedding-model cache (~79 MB, disposable) so search does not re-download.
- mempalace-shared-chroma:/data/.cache/chroma
networks:
- mempalace-net
healthcheck:
# A tools/list round-trip proves the server is answering MCP (python3 is
# always present — mempalace itself is a python tool in the image).
test:
- CMD
- python3
- -c
- "import urllib.request,json; d=json.dumps({'jsonrpc':'2.0','id':1,'method':'tools/list','params':{}}).encode(); r=urllib.request.Request('http://127.0.0.1:8765/mcp',data=d,headers={'Content-Type':'application/json','Accept':'application/json'}); urllib.request.urlopen(r,timeout=5).read()"
interval: 30s
timeout: 10s
retries: 3
start_period: 60s
volumes:
mempalace-shared:
mempalace-shared-chroma:
networks:
mempalace-net:
name: mempalace-net
+4 -1
View File
@@ -76,7 +76,10 @@ services:
# Persist uv data (Python installs, tool installs) # Persist uv data (Python installs, tool installs)
- devbox-uv:/home/developer/.local/share/uv - devbox-uv:/home/developer/.local/share/uv
# Optional: persist MemPalace data (conversation memory, knowledge graph) # Optional: persist MemPalace data (conversation memory, knowledge graph).
# Applies to the LOCAL palace only (the default). In EXTERNAL mode
# (MEMPALACE_REMOTE_URL set in .env) the shared server owns the data, so
# this volume is irrelevant.
# - devbox-palace:/home/developer/.mempalace # - devbox-palace:/home/developer/.mempalace
# Optional: persist ChromaDB embedding model cache (~79 MB) # Optional: persist ChromaDB embedding model cache (~79 MB)