Both corrections come from the first real Phase 1 start on synlig (2026-08-12).
1. The Pangolin resource target was documented as bare `172.17.0.1:8765` with no
scheme, and the obvious guess from that is `https://` -- which cannot work.
`contrib/systemd/mempalace-serve.service` runs `serve --host 172.17.0.1
--port 8765` with no --tls-cert, so the primary speaks plaintext HTTP; TLS
terminates at Pangolin, which is the whole point of the RFC 6.2 decision.
Point a proxy at https:// and it attempts a TLS handshake against a plaintext
listener: 502 from outside, while `curl 172.17.0.1:8765/healthz` on the box
still says ok -- a confusing pair of symptoms. Now spelled `http://` with the
failure mode named, in the runbook and in the unit's comments.
2. `curl -s 127.0.0.1:8765/healthz` was documented as "expect 403". Wrong: the
real run returns empty. With a docker0-only bind nothing is listening on
loopback, so the connection is refused at TCP level before any header is sent
(%{http_code} -> 000, exit 7). The 403 is the *loopback-bind* case verified
2026-08-10 -- server on 127.0.0.1 answering a proxy-forwarded foreign Host.
Two distinct behaviours had been collapsed into one expectation in three
places (both runbooks and the unit).
Worth stating why the correction matters rather than just fixing it: refusal
is the *stronger* signal. A 403 proves only that a request was rejected; a
refused connection proves the loopback and LAN surface is not listening at
all. Someone who expected 403, saw silence, and "fixed" it by rebinding to
0.0.0.0 would have converted a correct configuration into an exposed one.
The docs now also say what to do if it hangs, or if ss shows 0.0.0.0:8765.
mempalace-toolkit
Producer-side tooling for MemPalace — bridges that feed opencode session history and project documentation into the palace. Pairs with the consumer-side mempalace agent skill.
What this repo contains:
bin/mempalace-session— exports opencode session history from its local SQLite DB to Claude Code JSONL, then mines it viamempalace mine --mode convos.bin/mempalace-pi-session— the same idea for the pi coding-agent: exports its native JSONL session transcripts and mines them the same way. Unlikemempalace-session, this one is normally invoked for you — the pi bridge extension (below) runs it automatically onsession_shutdownand a debouncedagent_settled, so most pi machines never need thecontrib/scheduling templates at all. Seemempalace-pi-sessionbelow andextensions/pi/README.md§ Automatic transcript feeding.bin/mempalace-docs— mines project directories into MemPalace while excluding source code, keeping the palace signal-dense.ARCHITECTURE.md— canonical spec: architecture diagram, component details, setup recipe, operational notes, upstream-retirement roadmap.SKILL.md— the companion agent skill, symlinked into~/.agents/skills/opencode-mempalace-bridge/on install.extensions/pi/— the pi↔mempalace MCP bridge (a TypeScript extension symlinked into~/.pi/agent/extensions/). Pi's own base config (keybindings, env loader, settings template) is in the siblingpi-toolkitrepo — split out 2026-05-05 soopencode-devboxcan build slim containers without mempalace.
If you're just trying to get this working on a new machine → jump to Setup.
If you want the full architecture story → read ARCHITECTURE.md.
Ecosystem
This repo is the memory-layer hub in a family of composable toolkits.
Each install.sh is independent and idempotent; install only what you
need. MemPalace can be opted out entirely (the opencode-devbox slim
container path exercises this).
┌──────────────────┐
│ myconfigs │ dotfiles: ~/.config/pi/.env, ~/.config/opencode/.env,
└────────┬─────────┘ tmux CSI-u, zsh loaders, … (git-crypt encrypted)
│ provision
┌──────────────┼──────────────┐
▼ ▼ ▼
┌──────────┐ ┌──────────┐ ┌──────────────┐
│opencode- │ │pi-toolkit│ │ (pi/opencode │
│ toolkit │ │ │ │ installed │
│ │ │ │ │ upstream) │
└─────┬────┘ └─────┬────┘ └──────────────┘
│ │
│ (optional) │ (optional)
└──────┬───────┘
▼
┌─────────────────┐
│mempalace-toolkit│ ← this repo
└─────────────────┘ detects pi / opencode and installs bridges
for whichever are present
Who owns what:
| Repo | Scope |
|---|---|
myconfigs |
Dotfiles (git-crypt encrypted). Ships .env files each toolkit below sources. |
opencode-toolkit |
Opencode's own shell glue (env loader for ~/.config/opencode/.env). No mempalace dep. |
pi-toolkit |
Pi's own base config (keybindings, env loader, settings.example.json). No mempalace dep. |
mempalace-toolkit (this) |
Memory layer. Mining wrappers in bin/, opencode-mempalace-bridge skill, pi↔mempalace MCP extension. Probes for the other toolkits' artifacts; installs bridges where relevant. |
opencode-devbox |
Docker containers that compose any subset via independent install.sh invocations. |
Why this exists
MemPalace is the agent memory layer. Its stock CLI has two gaps that bite on a machine running opencode with a docs-first palace policy:
mempalace minefloods the palace with source code — every__init__fragment, every generated file, hundreds of low-signal drawers per project.mempalace-docsfixes this by staging only documentation-class files (*.md,*.yml,Dockerfile, etc.) before mining.mempalace mine --mode convoscan't read opencode's SQLite DB — only file-based chat formats (Claude Code JSONL, Claude.ai JSON, ChatGPT, Slack, Codex). Opencode persists every turn in~/.local/share/opencode/opencode.dband has no upstream hook into mempalace's auto-save.mempalace-sessionfixes this by exporting each session to Claude Code JSONL before mining.
Both wrappers follow the same stage-to-cache-then-mine idiom. Neither reimplements the miner; they curate input and delegate.
Long-term, both should retire:
mempalace-docs→ retires when MemPalace PR #1213 (exclude_patternsinmempalace.yaml) merges.mempalace-session→ retires when opencode session-stopping hooks (PR #16598 et al.) merge andhooks_cli.pygains anopencodeharness. Until both land, this repo fills the gap.
See ARCHITECTURE.md §6 for the full upstream roadmap.
Setup
Prerequisites
- MemPalace CLI v3.3.3+ — see Installing mempalace itself below if you haven't already.
- Python 3 (stdlib
sqlite3only — no extra deps) - opencode with an active session DB at
~/.local/share/opencode/opencode.db(only needed formempalace-session) - The mempalace wake-up protocol at
~/.config/opencode/instructions/mempalace.md— without it, opencode loads the mempalace skill but never auto-runs it at session start, so most of mempalace's value is forfeited silently. Shipped by the skillset repo; deploy via./deploy-skills.sh --bootstraponce per machine.mempalace-toolkit/install.shprobes for this file and warns if it's missing. - The mempalace MCP server registered in
~/.config/opencode/opencode.json— without this, opencode has no way to reach the mempalace tools and everymempalace_*call silently fails. See Registering mempalace with opencode below for the one-line JSON stanza.install.shprobes for this too.
Installing mempalace itself (prerequisite)
mempalace-toolkit wraps the mempalace CLI but does not bundle it. The upstream MemPalace repo documents pip install mempalace as the install method; uv tool install is cleaner and is the flow used in production containers like opencode-devbox.
Why uv over pip:
- Isolated venv per tool — mempalace's dependencies (chromadb, embedding model runtime, …) don't leak into system Python or your project venvs.
- No PEP 668 fight — modern Debian / Ubuntu / Homebrew Python all refuse
pip installinto the system site-packages.uv tool installsidesteps this entirely. - The shim (
~/.local/bin/mempalaceby default) is a thin wrapper that automatically activates the isolated venv on invocation, somempalaceis available from any bash or zsh terminal without manualsource venv/bin/activate.
Install uv if it's not already on the machine:
# macOS / Linux, official installer — puts uv in ~/.local/bin
curl -LsSf https://astral.sh/uv/install.sh | sh
# Or: Homebrew on macOS
brew install uv
# Verify
uv --version
Personal machine (recommended default)
# Installs mempalace into an isolated venv under ~/.local/share/uv/tools/mempalace/,
# puts the `mempalace` shim into ~/.local/bin/.
uv tool install mempalace
# Make sure ~/.local/bin is on $PATH (uv prints this if it isn't)
export PATH="$HOME/.local/bin:$PATH" # add to ~/.bashrc or ~/.zshrc
# Verify
mempalace --version # should print the installed version
which mempalace # should point into ~/.local/bin/
After this, mempalace works the same from any bash or zsh terminal — interactive shell, script, cron, systemd user service, launchd agent, all fine.
To upgrade later: uv tool upgrade mempalace (or --all).
To uninstall: uv tool uninstall mempalace.
System-wide / container install (opencode-devbox pattern)
For a Docker image or a multi-user box where the shim should live on the system PATH rather than in each user's ~/.local/bin, use UV_TOOL_DIR + UV_TOOL_BIN_DIR to relocate both the venv and the shim:
# In the Dockerfile — this is the pattern used by opencode-devbox
ENV UV_TOOL_DIR=/opt/uv-tools
ENV UV_TOOL_BIN_DIR=/usr/local/bin
RUN mkdir -p /opt/uv-tools && \
uv tool install --no-cache mempalace && \
/opt/uv-tools/mempalace/bin/python -c "import mempalace; print('mempalace installed')"
After this:
/opt/uv-tools/mempalace/— the isolated venv./usr/local/bin/mempalace— the CLI shim (globally onPATH, works for every user).
The last python -c line in the RUN step is a build-time sanity check: if the install silently failed, the build fails here rather than at runtime.
See opencode-devbox/Dockerfile §"MemPalace install" for the full production version (adds INSTALL_MEMPALACE=true build arg so the install can be skipped to shave ~300 MB off the image).
Registering mempalace with opencode (or other MCP clients)
Installing mempalace via uv tool install puts two shims on PATH: mempalace (the CLI) and mempalace-mcp (the MCP server). Neither the skill nor mempalace-toolkit will give you anything useful in opencode until the MCP server is registered in ~/.config/opencode/opencode.json.
Add to opencode.json:
{
"$schema": "https://opencode.ai/config.json",
"mcp": {
"mempalace": {
"type": "local",
"command": ["mempalace-mcp"]
}
}
}
If you already have other MCP servers configured, add the mempalace entry into the existing mcp object — don't replace the whole file. opencode's JSON is merged shallow-ly.
Minimal full opencode.json for someone starting fresh (adjust model to your preferred provider):
{
"$schema": "https://opencode.ai/config.json",
"model": "anthropic/claude-opus-4-6",
"share": "disabled",
"autoupdate": false,
"instructions": [
"~/.config/opencode/instructions/mempalace.md"
],
"mcp": {
"mempalace": {
"type": "local",
"command": ["mempalace-mcp"]
}
}
}
Note the instructions array: this is what tells opencode to load the wake-up protocol at session start (see the previous section on installing that file via skillset --bootstrap).
Custom palace path (rare — default ~/.mempalace/palace/ works for everyone):
"command": ["mempalace-mcp", "--palace", "/path/to/palace"]
Claude Code has a one-liner helper:
claude mcp add mempalace -- mempalace-mcp
…or mempalace mcp prints the current recommended snippets on demand:
mempalace mcp # shows `claude mcp add` + direct-run commands
After editing the config, restart opencode (or your MCP client). Verify the connection:
# From inside an opencode session:
# mempalace_status → should return palace stats, not "tool unavailable"
If the MCP tools don't show up in your agent's tool list, the most common causes are:
| Symptom | Likely cause | Fix |
|---|---|---|
mempalace_* tools absent from tool list entirely |
opencode.json not re-read | Restart opencode. |
| Server reported "unavailable" at startup | Shim not on PATH, or CLI itself broken |
Run mempalace-mcp --help manually; fix PATH or re-run uv tool install mempalace. |
ModuleNotFoundError: No module named 'mempalace' in logs |
You configured ["python3", "-m", "mempalace.mcp_server"] instead of ["mempalace-mcp"] |
Use the shim. See the legacy-fallback section below if you specifically need the python-invocation form. |
Legacy fallback: mempalace-mcp-server wrapper
Older MCP configs sometimes reference ["python3", "-m", "mempalace.mcp_server"]. This worked when mempalace was installed via pip install --break-system-packages into the system site-packages, but breaks after switching to uv tool install — system python3 cannot import mempalace from the isolated venv. On opencode-devbox a thin wrapper script on PATH bridged the two worlds during the transition:
#!/bin/sh
# /usr/local/bin/mempalace-mcp-server
exec /opt/uv-tools/mempalace/bin/python -m mempalace.mcp_server "$@"
With the wrapper in place, MCP configs referencing ["mempalace-mcp-server"] work on a uv tool install setup.
This is legacy; don't use it for new installs. The modern ["mempalace-mcp"] form — the uv-tool shim — does the same job without the extra script. The wrapper is documented here only so you know what to look for if you encounter an older config or a machine that was set up during the transition.
Verification checklist
After any install (personal or system-wide), confirm:
# CLI reachable from PATH
which mempalace # → a shim path
mempalace --version # → v3.3.3+ without import errors
# CLI can import its own modules (catches venv vs site-packages mismatch)
mempalace status 2>&1 | head -3 # → either palace stats or "No palace found" — not a Python traceback
# MCP shim reachable and runnable
which mempalace-mcp # → a shim path matching the `mempalace` CLI location
mempalace-mcp --help 2>&1 | head -5 # → MCP server help, not a Python traceback
If any of these produce ModuleNotFoundError, the isolated venv is broken — re-run uv tool install --force mempalace (or the system-install equivalent with UV_TOOL_DIR set) and check the verification again.
Install mempalace-toolkit
git clone ssh://git@gitea.jordbo.se:2222/joakimp/mempalace-toolkit.git ~/mempalace-toolkit
cd ~/mempalace-toolkit
./install.sh
The installer symlinks bin/* into ~/.local/bin/ and installs the agent skill into ~/.agents/skills/opencode-mempalace-bridge/. If pi is installed (detected via ~/.pi/agent/extensions/), it also symlinks extensions/pi/mempalace.ts into that directory so the pi↔mempalace bridge tracks version control. On machines without pi this step is silently skipped. Works on macOS and Linux.
Ensure ~/.local/bin is on $PATH:
export PATH="$HOME/.local/bin:$PATH"
If install.sh reports Skipping <name> — already exists: there's a leftover symlink or file at ~/.local/bin/<name> from a previous install (e.g. the pre-split cli_utils days). The installer prints the exact rm && ./install.sh command to fix it — remove the stale entry and re-run. It will never clobber an existing file without the user explicitly removing it first.
Deploying pi on a new machine (full recipe)
If the target machine also runs pi, the recipe is: install pi-toolkit first (pi's own base config), then this toolkit (adds the pi↔mempalace MCP bridge). Full step-by-step: extensions/pi/README.md § Deploying pi with mempalace on a new machine.
Quick summary:
# 1. Dotfiles (tmux extended-keys, ~/.config/pi/.env, ...)
git clone <myconfigs> && cd myconfigs && ./provision.sh --profile <profile>
# 2. pi upstream 3. pi-toolkit (pi base config)
brew install pi-coding-agent git clone ssh://git@gitea.jordbo.se:2222/joakimp/pi-toolkit.git
cd pi-toolkit && ./install.sh
# 4. settings bootstrap
cp ~/pi-toolkit/settings.example.json ~/.pi/agent/settings.json && $EDITOR !$
# 5. mempalace CLI 6. This repo (adds the bridge)
uv tool install mempalace cd ~/mempalace-toolkit && ./install.sh
# 7. Open fresh shell, run `pi`. Wake-up auto-injection proves end-to-end.
First mine
# Mine opencode session history into wing_conversations (no init needed)
mempalace-session --dry-run # preview qualifying sessions
mempalace-session # do it (~20 min per 60 sessions)
# Mine a project (docs only). If you want to pre-init the project with a
# custom wing name or entity config, run `mempalace init --yes <dir>` first;
# otherwise `mempalace-docs` derives the wing from the directory name.
mempalace-docs /workspace/my_project --dry-run
mempalace-docs /workspace/my_project
Note: mempalace has no one-time global init. The palace itself is created lazily on first write (at
~/.mempalace/palace/).mempalace init <dir>is a per-project command that sets up amempalace.yaml+ entity list for a specific source directory — optional, not a prerequisite for either wrapper.
Diary vs session mine: why keep both?
Automated session mining captures every turn verbatim into wing_conversations. But agents are still expected to write a short AAAK-compressed diary entry at wind-down (the consumer-side mempalace skill calls this out as mandatory). They're not redundant — they answer different questions:
- Session mine = git log with diffs. "What did we say exactly?" Raw, searchable, complete. High noise.
- Diary = release notes. "What did we decide / learn / accomplish?" Curated, compressed, recency-scanned. The agent's editorial judgment of what mattered, including meta-observations that were never said aloud.
A machine running only one of these has half a memory. Full treatment with practical implications in ARCHITECTURE.md §5 → "Diary vs session mine: why keep both?". Short answer: automate the mine, keep writing diaries, and let them specialize.
Keeping it fresh (automation)
pi: nothing to set up. The pi bridge extension
(extensions/pi/mempalace.ts) feeds the palace by itself — see
extensions/pi/README.md § Automatic transcript feeding.
The templates below aren't needed unless you're running
mempalace-pi-session somewhere without that extension (e.g. a bare pi
install, or a host-side catch-up job).
opencode, and pi installs without the bridge: manual invocation is fine
while you're actively driving the machine, but long-running devboxes benefit
from a weekly automated mine. contrib/ ships ready-to-install
templates:
- systemd user timer (recommended on Linux): survives reboots, catches missed runs, logs to
journalctl. - launchd user agent (recommended on macOS): native-equivalent — logs to
~/Library/Logs/, single-instance guarantees,ProcessType=Backgroundthrottling. - cron: simplest, works on BSD and systemd-less distros. No user-unit awareness needed.
- Devbox variants (
*-devbox.*): if you runmempalace-sessioninside a long-lived container (e.g. opencode-devbox), the scheduler lives on the host and usesdocker execto reach the tool inside the container. Systemd and cron variants are included; both guard against "container currently stopped" so the timer is safe to leave enabled across dev cycles.
Quick-start (Linux / systemd, weekly Mon 03:00 local):
mkdir -p ~/.config/systemd/user
cp contrib/systemd/*.{service,timer} ~/.config/systemd/user/
systemctl --user daemon-reload
systemctl --user enable --now mempalace-session.timer
sudo loginctl enable-linger "$USER" # optional, for headless boxes
Quick-start (macOS / launchd, same schedule):
sed "s|USER|$USER|g" contrib/launchd/se.jordbo.mempalace-session.plist \
> ~/Library/LaunchAgents/se.jordbo.mempalace-session.plist
mkdir -p ~/Library/Logs
launchctl bootstrap "gui/$(id -u)" ~/Library/LaunchAgents/se.jordbo.mempalace-session.plist
launchctl enable "gui/$(id -u)/se.jordbo.mempalace-session"
Quick-start (host-side scheduling for a long-running opencode-devbox container):
# systemd on the host → docker exec into the container
mkdir -p ~/.config/systemd/user
cp contrib/systemd/mempalace-session-devbox.{service,timer} ~/.config/systemd/user/
systemctl --user daemon-reload
systemctl --user enable --now mempalace-session-devbox.timer
# If your container isn't named 'opencode-devbox' or its user isn't
# 'developer', run `systemctl --user edit mempalace-session-devbox.service`
# to set CONTAINER / CONTAINER_USER via an override.
See contrib/README.md for full install/verify/uninstall recipes, tuning, chooser table (host vs. devbox), and devbox/container caveats. The full operational routine (triggers, cadence, verification) is in ARCHITECTURE.md §5.
Containerized (devbox) notes
On a Docker-based devbox, the palace and opencode DB should live on named volumes so they survive container recreate:
devbox-palace→~/.mempalace/palacedevbox-data→~/.local/share/opencode
This repo is typically bind-mounted from the host, so code survives recreate and syncs via git. After a container recreate, ~/.local/bin is wiped — just re-run ./install.sh (idempotent) to relink.
mempalace-docs
Docs-only MemPalace miner. Stages documentation files into a cache dir and runs mempalace mine against the cache — never against the raw project dir.
mempalace-docs <directory> # mine with wing = dirname
mempalace-docs <directory> --wing my_project # override wing name
mempalace-docs <directory> --agent alice # record agent on drawers
mempalace-docs <directory> --dry-run # list files, don't file
mempalace-docs <directory> --repair # opt-in post-mine repair (risky, interactive only)
mempalace-docs --help
What gets mined: *.md, *.mdx, *.rst, *.txt, *.yml, *.yaml, *.toml, *.json, *.sh, *.bash, *.zsh, *.fish, Dockerfile*, Makefile*, *.conf, *.cfg, *.ini, LICENSE*, COPYING*, NOTICE*.
What gets skipped: .py, .ts, .tsx, .js, .jsx, .go, .rs, .java, .cpp, .c, .rb, .kt, .swift, build output directories (.git, .venv, node_modules, __pycache__, .mypy_cache, .pytest_cache, .ruff_cache, dist, build, .next, target, coverage), lockfiles.
Rationale: the palace is for context and intent. Agents already have grep/glob/Read for code — always authoritative, never stale. Embedding source code creates a parallel, lossier, drift-prone copy that pollutes semantic search for years.
mempalace-session
Opencode → MemPalace session bridge. Reads ~/.local/share/opencode/opencode.db, transforms each session into Claude Code JSONL, and files via mempalace mine --mode convos.
mempalace-session # mine all sessions (≥3 msgs)
mempalace-session --wing my_convos # custom wing (default: wing_conversations)
mempalace-session --session ses_abc123 # one session only
mempalace-session --since 2026-04-01 # only sessions updated on/after date
mempalace-session --min-messages 6 # stricter short-session filter
mempalace-session --db /custom/path/opencode.db # non-default DB location
mempalace-session --dry-run # export + list, skip mine
mempalace-session --repair # opt-in post-mine repair (risky, interactive only)
mempalace-session --help
What gets exported per session:
- Synthetic header injected as the first user turn (
[session: <title> | <dir> | <date>]) so the palace can find sessions by topic, not just by ID. - Each message → Claude Code JSONL line (
{"type": "user"|"assistant", "message": {"content": ...}}). - Tool calls →
tool_useblocks. Known tools (Bash,Read,Grep,Edit,Write) get formatted summaries; unknown tools are JSON-serialized. - Tool outputs →
tool_resultblocks in a follow-up human message, folded back into the assistant turn by the mempalace normalizer. step-start/step-finishparts are dropped as noise.reasoningparts are kept with a[reasoning]prefix.
Dedup: staging at <palace-root>/opencode-stage/<wing>/ (override: $MEMPALACE_SESSION_STAGE) with deterministic per-session filenames (<slug>_<id>.jsonl). The convos miner keys on source_file, so re-runs skip unchanged sessions. To force re-mining a session, delete its JSONL from the staging dir.
--dry-run is dedup-aware. Each session is tagged [NEW] (would be filed) or [SKIP] (already in the palace), and the summary breaks down the count:
Exported 62 session(s) to /home/you/.mempalace/opencode-stage/wing_conversations
0 new → will be filed on mine
62 already filed → will be skipped (dedup by source_file)
--dry-run: no new sessions to mine. A real run would skip all 62.
If the palace is unreachable (fresh install, moved, permission-denied) the wrapper falls back to "everything is new" — the real mine step delegates dedup to mempalace mine --mode convos, which is always the source of truth. So running mempalace-session twice in a row is never destructive or wasteful: the second run's only cost is the post-mine HNSW repair step (~5 min on a ~5k-drawer palace).
Filter: sessions with fewer than --min-messages messages (default 3) are skipped — drops throwaway /exit'd sessions that would otherwise flood the palace. On a reference 140-session corpus, 78 were filtered this way.
Cost profile: ~20 minutes per 60-session batch. Scales roughly linearly with message count. Dedup re-run: mine step instant, only the post-mine repair runs (~5 min on 5k drawers).
mempalace-pi-session
Pi coding-agent → MemPalace session bridge. Reads pi's own JSONL session
transcripts under ~/.pi/agent/sessions/, converts each qualifying session
to Claude Code JSONL, and files via mempalace mine --mode convos. On most
machines you never run this by hand — the pi bridge extension
(extensions/pi/mempalace.ts) invokes it automatically; see
extensions/pi/README.md § Automatic transcript feeding.
Manual invocation is for a bare pi install without that extension, a
host-side catch-up job, or just poking at the export by hand.
mempalace-pi-session # export + mine everything qualifying
mempalace-pi-session --wing my_convos # custom wing (default: wing_conversations)
mempalace-pi-session --session <uuid-prefix> # one session only
mempalace-pi-session --since 2026-04-01 # only sessions updated on/after date
mempalace-pi-session --min-messages 6 # stricter turn-count filter (default: 4)
mempalace-pi-session --min-assistant-chars 2000 # stricter "did anything happen" filter (default: 1000)
mempalace-pi-session --dry-run # export + list, skip mine
mempalace-pi-session --prepare # export + stage only; print MINE_SOURCE=<path>, never opens the palace
mempalace-pi-session --mode remote # ship the stage to a remote palace host instead of mining locally
mempalace-pi-session --help
Why --prepare exists: the palace is single-writer. If a pi session for
this same palace is currently open, its own bridge extension already holds
the palace via mempalace-mcp, so an unattended mempalace mine from
anywhere else fails with palace ... is held by PID <n>. --prepare does
only the palace-free half (export, threshold, staging, and the rsync in
remote mode) and hands the mine off to whoever already holds the lock —
which is exactly what the pi extension does with its own MCP client. Run
without --prepare, the tool does the mine itself, and treats that
contention as success (exit 0, informational message) rather than failure —
the holder will mine what got staged.
What gets exported per session:
- Synthetic header injected as the first user turn
(
[session: <title> | <cwd> | <date> | source: pi]) so the palace can find sessions by topic, not just by ID, and so pi/opencode/other results stay distinguishable in search. - User/assistant messages extracted from pi's JSONL
messageentries. - Assistant
toolCallblocks → Claude Codetool_useblocks. toolResultrole messages →tool_resultblocks, folded back into the assistant turn by the mempalace normalizer.bashExecution,custom(display-only),branchSummary,compactionSummary→ rendered as text annotations.thinkingblocks and image content → dropped (noise / not text).
Filter — two gates, both required (this is stricter than
mempalace-session's single message-count filter, and deliberately so —
see below):
--min-messagesuser+assistant turns (default 4).--min-assistant-charscharacters of assistant text, tool results excluded (default 1000).
The second gate exists because message count alone isn't enough for pi: pi expands skill/context text into the user prompt, so an abandoned session can have a huge "user" turn and almost nothing on the assistant side — e.g. a real observed case with a 13,380-char injected-context user turn answered "Ready. What would you like to work on?" (38 chars). Total size said substantial; assistant size correctly said nothing happened. Measured real sessions on the same machine ran 15,900–100,000 assistant chars, so the 1000-char default sits with wide margin on both sides.
Dedup: staging under $MEMPALACE_PI_STAGE/<wing>/ (default
<palace-root>/pi-stage/<wing>/ — beside the palace, so the stage cannot be
wiped independently of the dedup keys pointing at it) with deterministic per-session-UUID
filenames, and the export preserves the source session's mtime on the
staged file. The convos miner is mtime-aware (see the Gotchas in
AGENTS.md — an older version of this doc claimed otherwise),
so re-runs on an unchanged session are a no-op, and re-feeding a grown
session (a live one being fed mid-conversation) purges and refiles that
session's drawers instead of duplicating them.
Staging must persist. Dedup keys on the staged path, not the original
transcript, so if the stage is wiped, a mempalace sync scoped to include it
sees those source files as gone and prunes the drawers mined from them —
deleting the memories, not just the cache. This is why the stage now defaults to
<palace-root>/pi-stage: it inherits whatever persistence the palace has, so
the files and the dedup keys that reference them cannot be separated by
wiping something that merely looks disposable. Override with
$MEMPALACE_PI_STAGE only if you have somewhere more durable than the palace.
Remote palace: if $MEMPALACE_REMOTE_URL is set, there is no
remote-palace CLI to mine into directly — mempalace_mine expands its
source path in the server process, which cannot see this machine's staged
exports. --mode remote (or --mode auto, which detects
$MEMPALACE_REMOTE_URL) instead rsyncs the stage to a per-device inbox on
the palace host and asks the server to mine its own local copy. Requires
$MEMPALACE_PI_SSH_TARGET (user@host:path); see MEMPALACE_PI_SSH_CONFIG,
MEMPALACE_PI_REMOTE_PATH, and MEMPALACE_PI_DEVICE in --help for the
rest. Deploying that primary — newt, DNS, and why the auth is a shared bearer
token rather than per-device proxy users — is
docs/phase-1-exposure-runbook.md.
Companion agent skill
Installing this repo symlinks SKILL.md into ~/.agents/skills/opencode-mempalace-bridge/SKILL.md, where it's auto-discovered by opencode (and by Claude Code / Kiro if you run agents-sync from cli_utils).
The skill is the short-form checklist for agents — when to use which wrapper, failure modes, setup recipes, anti-patterns. The canonical reference is always ARCHITECTURE.md; the skill points there for deep context.
The skill pairs with the consumer-side mempalace skill — that one covers using the palace (search, diary, KG); this one covers feeding it.
Colocated skill pattern. The skill lives here (not in skillset) because it moves in lockstep with the wrappers it documents. install.sh drops a .skill-source marker file in the deployed skill directory so sibling tooling (skillset's deploy-skills.sh, cli_utils's agents-sync.zsh) can tell the directory is externally owned. See AGENTS.md for the full convention and how to adopt it for future colocated skills.
See also
ARCHITECTURE.md— canonical spec: diagrams, setup recipe, failure modes, upstream roadmap.AGENTS.md— repo conventions for AI agents modifying this codebase.- MemPalace — the memory layer itself.
- opencode — the agent harness this bridges.
- cli_utils — sibling repo with shell quality-of-life tools (origin of these wrappers before the 2026-04-30 split).