Compare commits

2 Commits

Author SHA1 Message Date
jason b443ab8045 br0 setup 2026-05-09 11:10:17 -05:00
jason 2cd6e096a2 Merge pull request 'docs and usage' (#1) from develop into main
Reviewed-on: #1
2026-05-09 11:00:27 -05:00
+411
View File
@@ -0,0 +1,411 @@
# MemPalace on Unraid — GUI install with `br0` networking
This walks through installing MemPalace as a single Docker container on
Unraid, using the **dockerMan WebUI** (no compose, no SSH-driven YAML),
attached to **`br0`** so the container has its own IP address on your
LAN — separate from Unraid's IP.
If you'd rather do an SSH/compose install with TLS + bearer auth via a
Caddy sidecar, see [`README.md`](README.md). This guide is the
GUI-friendly path.
---
## Why `br0`?
`br0` (macvlan-style bridge to your physical network) gives the
container its **own LAN IP**. That means:
* No port conflicts with Unraid itself (you can keep `8765`/`8766`
instead of remapping).
* Clients connect to the container directly — `http://<container-ip>:8765/sse`
rather than `http://<unraid-ip>:8765/sse`.
* The container can be reached by every machine on your LAN without
punching holes in the Unraid host.
The trade-off, classic macvlan:
* By default the **Unraid host itself can't talk to the container**.
If you only ever connect from other machines, this is fine. If you
also want to test from Unraid's own shell, enable **Host access to
custom networks** (see Step 1).
* The container is exposed bare to the LAN. Anyone on the LAN can hit
the MCP endpoint — it has no auth in this mode. See "Security
notes" at the bottom.
---
## Prerequisites
* Unraid 6.12+ with Docker enabled.
* The repo synced to a path on Unraid (e.g. `/mnt/user/system/build/mempalace`).
You can clone over SSH or just copy the folder via SMB.
* `/mnt/user/appdata` available (default on every Unraid).
* A free LAN IP for the container, ideally with a DHCP reservation on
your router so it doesn't drift.
---
## Step 1 — Enable br0 in Unraid
Skip this if you already use `br0` for other containers.
1. **Stop the Docker service** so settings are editable: *Settings →
Docker → Enable Docker = No → Apply*.
2. *Settings → Docker → Advanced View*.
3. **Host access to custom networks** = **Enabled**. (Lets the Unraid
shell reach `br0` containers — needed for `curl` health checks
from Unraid itself. Skip if you don't need that.)
4. **Docker custom network type** = **macvlan** (default) or
**ipvlan** if your switch/router doesn't like macvlan MAC churn.
5. Re-enable Docker: *Enable Docker = Yes → Apply*.
6. Confirm `br0` shows up under *Settings → Docker → Docker custom
networks* (or implicitly — every Unraid with a bridge has it).
---
## Step 2 — Build the image on Unraid
There's no public registry image — the container is built locally from
the repo's root `Dockerfile`. SSH into Unraid (or open the WebUI
terminal) and run:
```bash
cd /mnt/user/system/build/mempalace # wherever you put the repo
docker build -t mempalace-server:latest .
```
First build pulls `python:3.13-slim` and pip-installs `mempalace` +
`mcp-proxy` (~35 minutes). When it finishes:
```bash
docker images mempalace-server
# REPOSITORY TAG IMAGE ID CREATED SIZE
# mempalace-server latest <hash> <time> ~600 MB
```
You only repeat this step when you `git pull` an update.
---
## Step 3 — Create appdata directories
```bash
mkdir -p /mnt/user/appdata/mempalace
chown -R 99:100 /mnt/user/appdata/mempalace
```
The container runs as UID/GID **99:100** (`nobody:users` — the Unraid
appdata convention). Wrong ownership here is the #1 cause of "permission
denied" on first start.
---
## Step 4 — Add the container in the WebUI
1. *Docker → Add Container* (don't pick a template — fill it in
manually).
2. Fill the top section:
| Field | Value |
|---|---|
| **Name** | `MemPalace` |
| **Repository** | `mempalace-server:latest` |
| **Network Type** | **Custom: br0** |
| **Fixed IP address** | *(your chosen LAN IP, e.g. `192.168.1.50`)* |
| **Console shell command** | `Bash` |
| **Privileged** | `Off` |
| **Extra Parameters** | `--user 99:100` |
Use a fixed IP. macvlan + DHCP can race the DHCP server on
container restart and end up unreachable.
3. Leave **WebUI** blank (there isn't one). **Icon URL** optional:
`https://raw.githubusercontent.com/MemPalace/mempalace/develop/assets/mempalace_logo.png`
---
## Step 5 — Add the path mapping
Click **Add another Path, Port, Variable, Label or Device****Path**:
| Field | Value |
|---|---|
| **Name** | `Appdata` |
| **Container Path** | `/data` |
| **Host Path** | `/mnt/user/appdata/mempalace` |
| **Access Mode** | `Read/Write` |
This holds ChromaDB, the knowledge graph, the embedding-model cache,
and the inbox for ingested transcripts. Lose it = lose all memory.
---
## Step 6 — Add the ports
In `br0` mode the "host port" field is just a label — the container's
ports are published on **the container's own IP**, not Unraid's. Set
both to match the container port.
Click **Add another …****Port** twice:
| Name | Container Port | Host Port | Connection Type |
|---|---|---|---|
| `MCP SSE` | `8765` | `8765` | `TCP` |
| `Ingest HTTP` | `8766` | `8766` | `TCP` |
Skip the ingest port if you only use this from a single machine and
won't push transcripts in via HTTP hooks.
---
## Step 7 — Add environment variables
Click **Add another …****Variable** for each:
### Required
| Name | Key | Value |
|---|---|---|
| Palace path | `MEMPALACE_PALACE_PATH` | `/data/palace` |
### Recommended (enables transcript ingest)
| Name | Key | Value |
|---|---|---|
| Ingest port | `MEMPALACE_INGEST_PORT` | `8766` |
| Ingest bind | `MEMPALACE_INGEST_HOST` | `0.0.0.0` |
| Ingest token | `MEMPALACE_INGEST_TOKEN` | *a long random string* |
`MEMPALACE_INGEST_TOKEN` is optional but **strongly recommended in
`br0` mode** — without it, anyone on the LAN can POST transcripts
into your palace. Generate one with:
```bash
openssl rand -hex 32
```
…and save it; you'll set the same value as `MEMPAL_REMOTE_TOKEN` on
each client.
### Optional
| Name | Key | Default | Notes |
|---|---|---|---|
| Embedding device | `MEMPALACE_EMBEDDING_DEVICE` | *(auto)* | `cpu`, `cuda`, `dml`, or `coreml`. CUDA needs a rebuild with the `[gpu]` extra and the NVIDIA driver plugin. |
| Entity languages | `MEMPALACE_ENTITY_LANGUAGES` | `en` | Comma-separated, e.g. `en,es,de`. |
---
## Step 8 — Apply and verify
Click **Apply**. Unraid will start the container; first start takes
~10 s while ChromaDB initializes.
From any machine on your LAN (or from Unraid itself if you enabled
*Host access to custom networks*):
```bash
# Unauth liveness check on the ingest port:
curl http://<container-ip>:8766/healthz
# → {"status":"ok","version":"3.3.x"}
# Confirm token gating works (only relevant if you set MEMPALACE_INGEST_TOKEN):
curl -i http://<container-ip>:8766/ingest/transcript
# → HTTP/1.0 401 Unauthorized
```
For the MCP SSE endpoint:
```bash
curl -i http://<container-ip>:8765/sse
# Should hang open with `Content-Type: text/event-stream` —
# that's healthy. Ctrl-C to bail.
```
If both work, you're done. Container logs are under *Docker → MemPalace
→ Logs* in the WebUI, or `docker logs MemPalace` from the shell.
---
## Connect AI tools
Each client needs `mcp-proxy`:
```bash
uv tool install mcp-proxy
# or: pip install mcp-proxy
```
### Claude Code
`~/.claude.json` (or project `.mcp.json`):
```json
{
"mcpServers": {
"mempalace": {
"command": "mcp-proxy",
"args": ["http://<container-ip>:8765/sse"]
}
}
}
```
No bearer header on this path — the MCP endpoint is unauthenticated
in `br0` mode.
### Codex CLI — `~/.codex/config.toml`
```toml
[mcp_servers.mempalace]
command = "mcp-proxy"
args = ["http://<container-ip>:8765/sse"]
```
### Antigravity / Windsurf
Settings → AI → MCP Servers, or `~/.antigravity/mcp.json`:
```json
{
"mempalace": {
"command": "mcp-proxy",
"args": ["http://<container-ip>:8765/sse"]
}
}
```
### Smoke test
Start a session in any of those clients and ask:
> "Use mempalace_status to show palace stats."
You should get back a JSON blob with wing/room/drawer counts. A
connection error means wrong IP or the container isn't on `br0`.
---
## Set up auto-save hooks (optional)
Enables transcripts to flow into the palace automatically as you work.
Each client needs the env vars:
**Windows (PowerShell)**:
```powershell
[Environment]::SetEnvironmentVariable("MEMPAL_REMOTE_URL", "http://<container-ip>:8766", "User")
[Environment]::SetEnvironmentVariable("MEMPAL_REMOTE_TOKEN", "<the-ingest-token>", "User")
```
**macOS/Linux**: same exports in `~/.zshrc` / `~/.bashrc`.
Then wire `hooks/mempal_save_hook_remote.sh` and
`hooks/mempal_precompact_hook_remote.sh` into your client's hook
config — see the "Set up auto-save hooks" section in
[`README.md`](README.md), it's identical from this point on.
Note: these hooks talk **HTTP**, not HTTPS, since this guide doesn't
use Caddy. If `MEMPAL_REMOTE_URL` starts with `http://`, no TLS env
vars are needed.
---
## Security notes
`br0` puts the container on your LAN with no auth in front of MCP.
That's fine if and only if:
* You trust every device on your LAN (no untrusted IoT, no guest
network bridged in, no roommates).
* You've set `MEMPALACE_INGEST_TOKEN` so nobody can drop transcripts
in unprompted.
If those don't both hold, do one of these instead:
* **Run on a Tailscale subnet** rather than `br0` so only your
tailnet devices can reach it.
* **Use the Caddy compose path** in [`README.md`](README.md) to add
TLS + bearer-token auth on the MCP side too.
* **Front it with SWAG / Nginx Proxy Manager** if you already run
one — drop the bare `8765` exposure, route through the proxy with
bearer-token auth and SSE pass-through configured.
---
## Troubleshooting
### Container starts then dies immediately
99% of the time this is `/data` ownership. From Unraid shell:
```bash
ls -ld /mnt/user/appdata/mempalace
# Want: owner 99 (nobody), group 100 (users)
chown -R 99:100 /mnt/user/appdata/mempalace
```
Then *Docker → MemPalace → Restart*.
### "Address already in use" on start
Another container on `br0` is using the same IP. Pick a different
**Fixed IP address** in Step 4. (Port conflicts are not possible in
`br0` mode since each container has its own IP.)
### Unraid shell can't reach the container, but other machines can
Default macvlan behavior. *Settings → Docker → Host access to custom
networks → Enabled* (Step 1).
### Clients see SSE connect but tool calls hang
Network path is fine; this is usually a stale `mcp-proxy` install on
the client. `uv tool upgrade mcp-proxy` or `pip install -U mcp-proxy`.
### Embedding model download stalls on first request
The ~80 MB MiniLM ONNX model downloads from HuggingFace on first use.
Pre-warm it from the Unraid shell:
```bash
docker exec MemPalace python -c \
"from chromadb.utils.embedding_functions import ONNXMiniLM_L6_V2; ONNXMiniLM_L6_V2()(['warmup'])"
```
Subsequent uses load from cache (~50 ms).
### Port 8765 / 8766 already used by something else
In `br0` mode the container has its own IP, so this only happens if
you literally run a second mempalace on the same IP. Pick a different
fixed IP and restart.
---
## Updating
```bash
cd /mnt/user/system/build/mempalace
git pull
docker build -t mempalace-server:latest .
```
Then in the WebUI: *Docker → MemPalace → Force Update* (or just
*Restart*). Your palace data persists because it's on the
`/mnt/user/appdata/mempalace` volume, outside the container.
---
## Backups
Add `/mnt/user/appdata/mempalace/` to **CA Backup / Appdata Backup**.
Three subdirectories matter:
* `palace/` — ChromaDB vectors + SQLite metadata
* `kg/` — knowledge graph
* `inbox/` — uploaded transcripts (kept for re-mining)
Losing this directory = losing all memory. The image itself is
disposable — you can rebuild it from the repo any time.