mempalace: drop the stage ENV pin, fix the shared-server compose
Lint / actionlint (push) Successful in 21s
Lint / hadolint (push) Successful in 29s

The feeder now defaults to <palace-root>/pi-stage upstream, so pinning
MEMPALACE_PI_STAGE into ~/.pi here is unnecessary -- and was actively wrong. It
created a second convention that could still diverge from the palace: keep the
devbox-palace volume, drop devbox-pi-config, and a scoped `mempalace sync`
prunes every conversation drawer, because dedup keys on the staged path. Both
the ENV and the entrypoint export are gone; a comment explains why adding one
back re-introduces the split it was meant to fix.

docker-compose.mempalace.yml was broken on mempalace 3.6.0 in both directions:
  - `--host 0.0.0.0` with no token in the environment makes the server refuse
    to start, crash-looping under `restart: unless-stopped`.
  - Supply a token and the healthcheck's unauthenticated `tools/list` POST 401s,
    marking a perfectly healthy server unhealthy forever.
Now the token is required via ${MEMPALACE_REMOTE_TOKEN:?...} so it fails fast at
`docker compose up` with a readable message, and the healthcheck probes the
deliberately token-free /healthz. The "no authentication of its own" security
note has been stale since 3.6.0 and is replaced with the actual posture
(bearer token + Host pin + Origin allowlist), including why browser-shaped auth
must not be put in front of it.

Dockerfile.base: mempalace-pi-session symlinked onto PATH, with a build-time
`--help` check so a broken feeder fails the image build rather than the first
session.

smoke-test: assert the stage resolves beside the palace (default, and following
$MEMPALACE_PALACE_PATH) instead of asserting the removed ENV pin. The two
behavioural guards -- a synthetic session that must be captured, an abandoned
one that must not be -- are unchanged.

.env.example: recommend `mempalace serve` on the docker0 gateway rather than
`mempalace-mcp --transport http --host 0.0.0.0`, with the two binds to avoid.
This commit is contained in:
Joakim Persson
2026-08-12 17:04:14 +02:00
parent 7649d53f3b
commit 7c00dd6001
6 changed files with 265 additions and 13 deletions
+46 -3
View File
@@ -12,16 +12,59 @@ 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.
# docker-compose.yml to persist it across container recreation — that one
# volume now covers the mined conversation transcripts too, since the pi and
# opencode feeders stage inside the palace root (<palace-root>/pi-stage), so
# the staged files and the palace dedup keys pointing at them cannot be
# separated.
#
# 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
#
# Serve it with: mempalace serve --host 172.17.0.1 --port 8765
#
# NOT `mempalace-mcp --transport http --host 0.0.0.0`: `serve` is the turnkey
# wrapper that mints/keeps a bearer token (0600, passed via env so it stays out
# of `ps`) and can terminate TLS. Two binds to avoid:
# 0.0.0.0 - exposes the palace to the whole LAN.
# 127.0.0.1 - behind a tunnel this 403s every proxied request (the Host pin
# is only enforced on loopback binds) AND silently starts with
# no token at all, since auto-minting is gated on the bind being
# non-loopback. Bind the docker0 gateway: reachable from the host
# and its containers (so a newt/proxy container works), not from
# the LAN. Set MEMPALACE_MCP_HTTP_TOKEN explicitly server-side.
# MEMPALACE_REMOTE_URL=https://mempalace.example.com/mcp
# MEMPALACE_REMOTE_TOKEN=
# ── MemPalace: automatic capture of pi sessions ───────────────────────
# The mempalace.ts extension feeds this container's pi transcripts into the
# palace by itself: on session_shutdown, and on a debounced agent_settled so a
# crash loses at most one window rather than the whole session. The entrypoint
# also runs a catch-up at container start, which is the only thing that can
# recover transcripts after a hard kill (no handler runs on SIGKILL).
# Nothing below is required for the local-palace case; the defaults work.
#
# MEMPALACE_FEED=0 # disable automatic capture entirely
# MEMPALACE_FEED_DEBOUNCE_MS=600000 # min gap between mid-session feeds (10 min)
# MEMPALACE_FEED_WING=wing_conversations
#
# REMOTE PALACE ONLY (MEMPALACE_REMOTE_URL set above): the palace is on another
# host, and `mempalace_mine` resolves its source path in the SERVER process, so
# the server cannot see this container's transcripts. The feeder therefore
# rsyncs its staged exports into a per-device inbox on the palace host and asks
# the server to mine its own local copy. Without MEMPALACE_PI_SSH_TARGET the
# feeder is skipped (a remote palace with no inbox has nothing to mine).
# MEMPALACE_PI_SSH_TARGET where to rsync to, as user@host:path
# MEMPALACE_PI_REMOTE_PATH what that inbox is called ON THE SERVER — must be
# the container path if the server runs in Docker
# (see docker-compose.mempalace.yml)
# MEMPALACE_PI_DEVICE inbox subdirectory for this machine (default: hostname)
# MEMPALACE_PI_SSH_TARGET=user@palace-host:/srv/mempalace-feed
# MEMPALACE_PI_REMOTE_PATH=/data/feed
# MEMPALACE_PI_DEVICE=
# ── LAN access from the container (host-OS-agnostic) ─────────────────
# On VM-backed hosts (macOS OrbStack / Docker Desktop) the container can't
# reach the host's directly-attached LAN peers by default. The entrypoint