Installing? The steps are in the "For agents" section of the README; this page is background and detail.
For jerpint and whoever is pairing with him. This is the walkthrough for running Woltspace natively on a Mac — no Docker.
Stop the container first. Two things below depend on it: the Telegram bot token can only be polled by one process, and the instance lock is not reliable across a Docker bind mount. Details in native-and-container.md; the short version is that this walkthrough uses a fresh data root and never touches the container's.
What was and was not verified. Every command and every quoted output below was actually run — inside the Linux dev container, from this branch, with an isolated tool dir, npm prefix, data root, and port. What that cannot cover is macOS itself: Homebrew installs, Apple Silicon wheels, the macOS
ps/tmux builds, and Gatekeeper. Steps marked (untested on macOS) are the ones where the Mac could still differ.
You need three things on PATH: tmux, Node 22+, and uv.
tmux -V
node --version
uv --versionAnything missing (untested on macOS):
brew install tmux node
curl -LsSf https://astral.sh/uv/install.sh | shYou also need a harness you are already logged into — claude, codex, or
opencode. Native mode reuses that login in place; it never copies your
credentials anywhere. If you are not logged in yet, log in the normal way for
that CLI first.
The published packages are the way to install:
uv tool install 'woltspace'An AI agent installing Woltspace should set WOLTSPACE_DEFAULT_HARNESS to its
own registered harness (for example, claude or codex) before the first
woltspace start; this skips the redundant first-run harness question.
A fresh lodge installs the public MIT-licensed
jerpint/woltspace-starter-lodge
from its latest main at first start (not pinned to a release, so onboarding
can improve between releases; existing lodges never re-fetch it). Set WOLTSPACE_STARTER_SEED=none before the
first start to turn it off, or set another seed source explicitly. The default
install needs network access on first run; if offline, the failure is reported
and onboarding continues.
The browser terminal is part of the Python package. Install the optional
@woltspace/tui npm package only if you want the separate terminal cockpit.
If you are working on the platform itself, install from your checkout instead so you pick up local changes:
cd /path/to/woltspace
uv tool install .Check the installed package:
$ woltspace --version
woltspace 0.5.1
If
uv tool installwarns that its bin directory is not on your PATH, follow theexport PATH=...line it prints (or runuv tool update-shell) and open a new shell.
Three programs are called
woltspace, and on a native install two of them can sit ahead of the one you just installed:
<bundle>/container/bin/woltspace— the thin HTTP control client. It is on every session's PATH by design, because that is the directorynotify,push-viewandsession-regcome from. It now recognises a native install and execs the console script beside it for anything it does not serve itself, sowoltspace startandwoltspace doctorreach the right program even when this copy wins the PATH race.woltspaceat the root of a checkout — the container-era bash Docker launcher. If your shell rc puts the checkout ahead of~/.local/bin(export PATH="$HOME/woltspace:$PATH"is the usual line), plainwoltspace startruns this, and it will try to boot a container. It now refuses when a native control plane owns the data root — but the launcher is not the CLI you want on a native host either way.~/.local/bin/woltspace— the one you want.
which -a woltspaceshows the order. If a checkout is winning, drop thatexport PATHline from your shell rc, or move it after~/.local/bin.
Do not point the first native run at ~/.woltspace/wolts while the
container has it mounted. Use a new directory:
export WOLTSPACE_WOLTS_DIR=~/.woltspace/native-woltsConfirm what it resolved:
$ woltspace paths
wolts_dir: /Users/you/.woltspace/native-wolts
state_root: /Users/you/.woltspace/native-wolts/.space
install_root: /Users/you/.local/share/uv/tools/woltspace/lib/python3.11/site-packages/woltspace/_bundle
endpoint: http://127.0.0.1:7777
isolation: hostinstall_root pointing inside the installed package is correct — that is the
bundled server and web assets, not your checkout.
$ woltspace doctor
✓ python: Python 3.11.2
✓ package: runtime and web assets present
✓ data-root: /Users/you/.woltspace/native-wolts (nearest existing parent: /Users/you/.woltspace)
✓ tmux: /usr/bin/tmux
✓ harness: claude=/Users/you/.local/bin/claude, codex=/usr/local/bin/codex
✓ host-auth: claude, codex
✓ browser-terminal: embedded Python PTY bridge
✓ port: 127.0.0.1:7777 is availableDoctor only reads. Every failure names one command that fixes it; work down the list until it is clean.
Two checks worth understanding:
- host-auth is a warning, not a failure. It means Woltspace found an existing harness login it can reuse. If it says nothing was detected, log in with that CLI — Woltspace will never copy a credential file for you.
- data-root-sharing only appears if the directory is already claimed by
another instance, including a running container. If you see it, stop that
instance or pick a different
WOLTSPACE_WOLTS_DIR. - browser-terminal confirms that tmux is available to the embedded Python PTY bridge. Node, npm and the optional terminal cockpit are not involved.
$ woltspace start
🦫 woltspace
⛺ the lodge started
lights on in the den
🦫 http://127.0.0.1:7777
the lodge is open
🌲 tunnel disabled - the lodge stays on this machine
🪵 wolts: /Users/you/.woltspace/native-wolts
🪵 logs: /Users/you/.woltspace/native-wolts/.space/logs/control-plane.log
🪵 status: woltspace statusstart runs doctor first and refuses to launch if it fails. Add --port 7788
if 7777 is taken.
$ woltspace status
⛺ the lodge is open
state: healthy
endpoint: http://127.0.0.1:7777
🪵 tunnel: disabled
🪵 wolts: /Users/you/.woltspace/native-wolts
🪵 owner: your-macbook
🐾 adoption: 0 live · 0 orphaned · 0 unchanged
🐶 connector telegram: disabled · disabled
fix: Set channels.telegram = {"enabled": true, "token": "<bot token>"} in /Users/you/.woltspace/native-wolts/.space/platform/config.json (or export TELEGRAM_BOT_TOKEN).state: and endpoint: sit at column 0 on purpose — those two lines are what
scripts grep for, and --json carries everything else, including the pid and
instance id the human read leaves out. adoption: 0 live is right on a first
run — there are no sessions to adopt yet. On later restarts this is how you see
that live sessions were picked back up. The browser terminal is served inside
the control plane and therefore has no separate connector status.
The tunnel is off by default natively; nothing is published. To expose the
lodge deliberately, WOLTSPACE_PUBLIC_TUNNEL=true woltspace start.
Open http://127.0.0.1:7777 and the lodge should be there. Create a wolt from
the lodge and the split view opens with a terminal on the left. The first
thing you will see in it is Claude Code's own workspace trust prompt for
the new wolt directory — native sessions run bare claude (the container
pre-trusts with wclaude), and prompt mode is the point. Accept it and the
session continues; it has not hung.
$ woltspace tui --dry-run
source: local
package: @woltspace/tui@0.5.1
command: /Users/you/.npm-global/bin/woltspace-tuisource: local means it found your installed TUI and its identity checked out.
Then run it for real:
woltspace tuiFrom there: create or select a wolt, confirm the working directory and
permission policy it shows you, spawn, and attach. Detach with tmux's usual
Ctrl-b d and you are back at the list; the session keeps running.
If --dry-run says source: npx instead, it did not accept your local
binary and will try to download from the registry — which cannot work yet. It
tells you so:
$ woltspace tui --dry-run
source: npx
package: @woltspace/tui@latest
command: /usr/local/bin/npx --yes --package=@woltspace/tui@latest woltspace-tui
woltspace: ignoring /nonexistent/woltspace-tui — [Errno 2] No such file or directory: '/nonexistent/woltspace-tui'; resolving @woltspace/tui@latest through npx instead.
woltspace: if that fails because @woltspace/tui@latest is not published yet, install both artifacts from a checkout: uv tool install . && cd tui && npm pack && npm install -g ./woltspace-tui-*.tgzRedo step 2 if you see that.
Use a different bot token than the container's. One token can only be
long-polled by one process; the second poller gets 409 Conflict from
Telegram and goes deaf while still looking alive. Make a second bot with
BotFather for native testing.
Telegram and Slack dependencies are included in the default package since 0.5.4. The install in step 2 already includes them; no extra install is needed.
Then write $WOLTSPACE_WOLTS_DIR/.space/platform/config.json:
{
"channels": {
"telegram": {
"enabled": true,
"token": "123456:your-test-bot-token",
"allowed_users": ["your-telegram-user-id"]
}
}
}Restart and check:
woltspace restart
woltspace statusconnector telegram: running · Telegram · bundled connector … · pid N means it
is up. If it says degraded with a 409, the other bot is still polling that
token. If it says disabled · enabled but python-telegram-bot is not installed, you skipped the extra — this is the exact output when that happens:
connector telegram: disabled · enabled but python-telegram-bot is not installed
fix: Reinstall woltspace to restore its dependencies: `uv tool install --force woltspace` (from a checkout: `uv tool install --force .`), then `woltspace start`.Leave the tunnel off. Native defaults to no tunnel, and you should not reuse the container's named-tunnel token while the container runs: two cloudflared connectors on one named tunnel load-balance, so real traffic would start landing on the test instance.
$ woltspace stop
control plane stopped; tmux sessions untouchedThat last clause is deliberate: stopping the control plane does not kill your
wolts. Their tmux sessions keep running, and the next woltspace start
re-adopts them from the registry — woltspace status will show them under
adoption. To end a session, stop the session, not the control plane.
Once the fresh-root run works, the same commands against WOLTSPACE_WOLTS_DIR=~/.woltspace/wolts
(container stopped!) bring up your existing wolts. Several things the container
does at boot have no native equivalent yet — platform tools on PATH, .env
secrets for the bot, skill sync, the creatures. The full list with workarounds
is in native-and-container.md.
Start with woltspace doctor and woltspace status; between them they cover
most of it.
| Symptom | What it means |
|---|---|
start failed: doctor-failed with a list |
A prerequisite is missing; each line names its fix. |
port: 127.0.0.1:7777 is already in use |
Something else has the port — woltspace start --port 7788. |
woltspace already running: http://127.0.0.1:PORT |
An instance owns this data root. That URL is where it actually serves, which may not be the port you asked for. |
state: stale |
A previous control plane died without cleaning up. woltspace stop clears the metadata; it signals nothing and leaves tmux alone. |
data-root-sharing warning |
Another instance — likely the container — claims this directory. Stop it or use a different WOLTSPACE_WOLTS_DIR. |
serve failed: … is not mounted |
Container mode without the wolts mount. Not applicable to a native run. |
Connector degraded with a 409 |
Another process is polling that bot token. |
Connector failed after several restarts |
It could not stay up; read .space/logs/connector-<name>.log. |
Terminal pane says [tui] connection failed: tmux is not installed |
Install tmux, then restart Woltspace. woltspace doctor reports the same missing prerequisite. |
| Terminal pane says its named tmux session does not exist | The browser will ask the lodge to resume that session and reconnect; if it repeats, inspect the session record and control-plane log. |
The control plane's own log is $WOLTSPACE_WOLTS_DIR/.space/logs/control-plane.log.
The point of native mode is that your harness login is reused, never copied. After a first run, the data root should contain state and logs and nothing resembling a credential:
$ find "$WOLTSPACE_WOLTS_DIR" \( -name "*credential*" -o -name "auth.json" \) | wc -l
0
$ find "$WOLTSPACE_WOLTS_DIR"
<wolts>
<wolts>/.space
<wolts>/.space/logs
<wolts>/.space/logs/control-plane.log
<wolts>/.space/logs/connector-tui.log
<wolts>/.space/platform
<wolts>/.space/platform/adoption.json
<wolts>/.space/platform/connectors.json
<wolts>/.space/platform/control-plane.json
<wolts>/.space/platform/control-plane.lockNo tunnel.json either: natively the tunnel is off unless you turned it on.
If a credential file ever shows up there, that is a bug worth stopping for.