operate / the operator's manual

One pod. Eight commands.

Everything you need to run, restart, update, pair, debug, and remove a mazemaker pod. Every command on this page is verified against installer/linux/install.sh — if the install script changes, this page changes the same day. No aspirational syntax. No "should work" handwaving.

01 / install.

1. Install.

One curl. Pre-flight checks run first — if rootless podman, systemd-user, or GPU prerequisites are missing, the installer tells you exactly what to fix before touching anything.

curl -fsSL https://mazemaker.online/install.sh | bash

The installer is idempotent — re-running it is the standard "update" path (§4). It detects existing state and skips work that's already done. Your data is never overwritten.

--no-start              # install but don't start the pod (configure first)
--refresh               # re-sync compute.toml + runtime.env, skip image rebuild
--check                 # dry-run: print what would happen, install nothing
--reset-fingerprint     # wipe local fingerprint+keypair+license, re-onboard fresh
--reconfigure           # re-prompt for embedding + Hermes (backs up existing)
--uninstall             # stop + remove units (keeps your data on disk)
--backend sqlite        # opt out of Postgres/pgvector (CPU-only path)
--remote URL --key KEY  # bind to a backend non-interactively (CI / scripts)
--help                  # print the full reference
  1. Pre-flight — verifies podman 4+, systemd-user, disk space, optional CUDA. Bails early with a fixable error if anything's missing.
  2. Fingerprint — derives a stable hardware fingerprint, generates an Ed25519 keypair in ~/.mazemaker/keys/ (mode 0600). This is the binding identity that pins your license.
  3. Browser onboard — opens mazemaker.dev, you sign in (passkey or mzm-* key), the backend issues a license JWT bound to your fingerprint, the JWT lands at ~/.mazemaker/license.jwt.
  4. Embedding choice — interactive prompt for the embedding provider (FastEmbed local / SentenceTransformers / Voyage / OpenAI / etc.). BYOK if not local.
  5. Quadlet install — copies .container units into ~/.config/containers/systemd/, runs systemctl --user daemon-reload.
  6. Image build — rootless podman builds localhost/mazemaker-v2-{mcp,embedding-worker,license-client,wonderland,dream-worker}. ~3–6 min on first install, ~30s on refresh.
  7. Pod bootsystemctl --user start mazemaker-pod.service starts the whole stack. Health checks confirm everything came up green.
  8. Architect link — final message gives you the dashboard URL (architect.mazemaker.dev) which talks loopback to your fresh pod.

First install end-to-end: ~12 min on a warm machine. Re-installs (which is the update path): ~45s.

02 / start, stop, status.

2. Start, stop, status.

The pod is a standard rootless-systemd-user unit set. Everything else flows from systemctl --user.

Day to day you touch exactly two knobs — the mazemaker CLI wraps the units so you never memorise them.

mazemaker on          # knob 1: start the always-on memory layer
mazemaker off         # knob 1: stop everything cleanly (pod + dream)
mazemaker dream on    # knob 2: INTERNAL — engine consolidates in-process
mazemaker dream off   # knob 2: EXTERNAL — hand off to the dedicated worker
mazemaker dream none  # knob 2: stop all consolidation
mazemaker status      # both knobs at a glance

Seven units underneath. Six form the always-on pod; mazemaker-dream-worker.service is on-demand (knob 2). mazemaker-pod.service is the parent.

mazemaker-pod.service                # parent of the always-on pod
mazemaker-pgvector.service           # Postgres + pgvector (Pro/Enterprise)
mazemaker-license-client.service     # license refresh + tier-gate sidecar
mazemaker-embedding-worker.service   # embedding model serving (CPU / GPU)
mazemaker-mcp.service                # MCP server — what Claude Code talks to
mazemaker-wonderland.service         # encrypted MCP proxy + federation (port 8765)
mazemaker-dream-worker.service       # ON-DEMAND — dream cycles + DAE + Stage S (Pro+)

Prefer mazemaker on|off. Under the hood, a clean stop needs --job-mode=replace-irreversibly: the pod members carry Restart=on-failure, so a plain stop mazemaker-pod.service gets cancelled by the restart policy and the members revive. The CLI handles this for you.

mazemaker on                                       # bring up the whole stack
mazemaker off                                      # shut down cleanly (irreversible teardown)

# equivalent low-level calls:
systemctl --user start mazemaker-pod.service
systemctl --user --job-mode=replace-irreversibly stop 'mazemaker-*'
systemctl --user restart mazemaker-mcp.service     # restart just the MCP server
systemctl --user is-active mazemaker-pod.service mazemaker-mcp.service \
                          mazemaker-wonderland.service mazemaker-embedding-worker.service \
                          mazemaker-license-client.service

# All five always-on units should print 'active' (dream-worker is on-demand —
# inactive unless knob 2 is on). If any say 'failed' → tail logs (§6).

# Wonderland (your pod's loopback API) health endpoint:
curl -s http://127.0.0.1:8765/health | jq

# License-client health (separate sidecar, port 8767 inside container):
podman exec systemd-mazemaker-license-client curl -s http://localhost:8767/health

The installer enables the units at boot by default. If you opted out (or want to verify):

systemctl --user enable mazemaker-pod.service
loginctl enable-linger $USER     # pod survives logout (one-time)

03 / logs.

3. Logs.

Every unit logs to the systemd-user journal. journalctl --user is the front door.

# Tail the whole pod (everything multiplexed):
journalctl --user -u mazemaker-pod.service -f

# Tail just one unit:
journalctl --user -u mazemaker-mcp.service -f
journalctl --user -u mazemaker-license-client.service -f
journalctl --user -u mazemaker-wonderland.service -f
journalctl --user -u mazemaker-dream-worker.service -f

# Last hour of failures across all units:
journalctl --user --since "1 hour ago" -p err -u "mazemaker-*"

# Audit log (license-affecting events — issues, refreshes, bans, tripwires):
tail -f ~/mazemaker-v2-pod/data/audit.log | jq

04 / update.

4. Update.

Re-run the install one-liner. It's idempotent. Detects what's changed, rebuilds what needs rebuilding, restarts the affected units. Your data + license + fingerprint survive untouched.

curl -fsSL https://mazemaker.online/install.sh | bash

Pulls fresh source, rebuilds container images, copies new Quadlet units, runs systemctl --user daemon-reload, restarts the pod. ~45s on a warm machine.

curl -fsSL https://mazemaker.online/install.sh | bash -s -- --refresh

Re-syncs compute.toml + runtime.env + Quadlet units, skips image rebuild. Right call if you only changed a setting in mazemaker.dev/#/pod-control.

  1. Pulls the latest pod source from github.com/itsXactlY/mazemaker-v2-backend into the cached install dir.
  2. Rebuilds the five container images (unless --refresh) — only layers that changed are rebuilt thanks to podman build cache.
  3. Re-copies Quadlet .container + .service + .path units into ~/.config/containers/systemd/.
  4. systemctl --user daemon-reload picks up unit changes.
  5. systemctl --user restart mazemaker-pod.service + all six container units. restart, not start — bare start is a no-op on already-active units, restart is the only way to pick up a freshly-built image.
  6. Health check confirms everything came up green.

05 / pair a second pod.

5. Pair a second pod.

Federation = sovereign pods on different machines that opt in to cross-pod memory sync. Two-laptop, hub-and-spoke, or WWW-scale mesh — same paste-handshake model. Every relationship is a Bearer-key pair; nothing is auto-discovered.

# On pod A:
curl -s http://127.0.0.1:8765/peer/identity | jq

# Returns:
# {
#   "fp_hash": "c0e550...",
#   "peer_key": "fed-...",          ← Bearer key for incoming /peer/sync calls
#   "url":      "http://A-host:8765",
#   "issued_at": 1779730226
# }

Copy the entire JSON block. This is the invite. The peer_key is a credential — treat it like an SSH key: share it over a channel you trust.

# On pod B (the inviter on the receiving end):
curl -s -X POST http://127.0.0.1:8765/peer/add \
  -H 'Content-Type: application/json' \
  -d '{
    "name":     "alca-7G",
    "fp_hash":  "c0e550...",
    "peer_key": "fed-...",
    "url":      "http://A-host:8765"
  }'

Repeat steps 1–2 in the reverse direction (B's identity pasted into A's /peer/add). Federation is symmetric only when both sides have each other in ~/.mazemaker/peers.toml.

# On BOTH pods:
curl -s http://127.0.0.1:8765/peer/list | jq
# → both pods should appear, with status: "healthy"

curl -s http://127.0.0.1:8765/federation/status | jq
# → last_sync_at, peers_count, sync_errors_24h, queue_depth

Once paired:

  • Periodic sync/peer/sync background task fans memory deltas to every peer in peers.toml. Push-on-write semantics. Network-partition-tolerant (queues + retries).
  • On-demand recallPOST /peer/recall from any pod fans a recall to every peer, merges + dedupes results, returns top-K. Faster than waiting for the next sync tick.
  • Trust boundary — the peer_key Bearer authorises only /peer/sync + /peer/recall. It cannot trigger remembers, change settings, or read the license JWT.
  • RevocationPOST /peer/edit with {"name": "...", "enabled": false} pauses sync without forgetting the peer. Delete the peers.toml entry to fully revoke.

For the architecture behind this — why every relationship is a key pair, why nothing's auto-discovered — see /federation/.

06 / troubleshooting.

6. Troubleshooting.

Tripwire fingerprint ban (usually triggered by multiple license-client restarts in a short window — the jti chain breaks). Check the admin /#bans panel; if you're the operator, the ban can be lifted with one SQL. Recovery path: clear last_issued_jti for your fp, delete the ban row, restart license-client. See the admin /#bans panel for full context.

journalctl --user -u mazemaker-mcp.service --since "5 min ago"
podman logs systemd-mazemaker-mcp

9× out of 10 it's: missing env var, bind-mount path doesn't exist, or image needs a rebuild after a host-side bind-mount changed.

Your local license-client hasn't refreshed yet. Force it:

curl -X POST http://127.0.0.1:8765/license/refresh-now

Should return {"status":"refreshed"}. If it 502s, see "license refresh returns 403" above.

curl -fsSL https://mazemaker.online/install.sh | bash -s -- --reset-fingerprint

Wipes ~/.mazemaker/keys/ + license.jwt + the cached fingerprint, then re-runs the onboard flow. Memory data on disk is preserved — just the identity rotates.

podman system prune -a                            # frees old image layers
podman image ls | grep mazemaker                  # see what you've got

07 / uninstall.

7. Uninstall.

Two levels: stop + remove units (keeps data), or full wipe.

curl -fsSL https://mazemaker.online/install.sh | bash -s -- --uninstall

Stops all units, removes Quadlet files from ~/.config/containers/systemd/, removes container images. Leaves ~/mazemaker-v2-pod/data/ + ~/.mazemaker/ in place — reinstall = exactly the same identity + memory.

# Only run if you really want to lose your local identity + memory:
curl -fsSL https://mazemaker.online/install.sh | bash -s -- --uninstall
rm -rf ~/mazemaker-v2-pod/data/   # all memories, all backups
rm -rf ~/.mazemaker/              # keys, license, runtime.env, compute.toml

Re-installing after a full wipe = a brand new pod with a brand new fingerprint. Your peers will need to re-add you under the new identity.

08 / quick reference.

Quick reference.

install            curl -fsSL https://mazemaker.online/install.sh | bash
update             curl -fsSL https://mazemaker.online/install.sh | bash
config refresh     curl -fsSL https://mazemaker.online/install.sh | bash -s -- --refresh
start              systemctl --user start mazemaker-pod.service
stop               systemctl --user stop  mazemaker-pod.service
restart            systemctl --user restart mazemaker-pod.service
status (all)       systemctl --user is-active mazemaker-*.service
health             curl http://127.0.0.1:8765/health
tail logs          journalctl --user -u mazemaker-pod.service -f
force license      curl -X POST http://127.0.0.1:8765/license/refresh-now
get peer ID        curl http://127.0.0.1:8765/peer/identity
list peers         curl http://127.0.0.1:8765/peer/list
fed status         curl http://127.0.0.1:8765/federation/status
uninstall          curl -fsSL https://mazemaker.online/install.sh | bash -s -- --uninstall
reset identity     curl -fsSL https://mazemaker.online/install.sh | bash -s -- --reset-fingerprint